]>
Commit | Line | Data |
---|---|---|
b614e16c MY |
1 | /* |
2 | * Copyright (C) 2011-2014 Panasonic Corporation | |
3 | * Author: Masahiro Yamada <yamada.m@jp.panasonic.com> | |
4 | * | |
5 | * SPDX-License-Identifier: GPL-2.0+ | |
6 | */ | |
7 | ||
8 | #include <common.h> | |
9 | #include <asm/io.h> | |
a86ac954 | 10 | #include <mach/ddrphy-regs.h> |
b614e16c MY |
11 | |
12 | void 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 */ | |
36 | tmp &= ~DTCR_RNKEN_MASK; | |
37 | tmp |= (1 << (DTCR_RNKEN_SHIFT + rank)) & DTCR_RNKEN_MASK; | |
38 | writel(tmp, p); | |
39 | } | |
40 | ||
41 | struct ddrphy_init_sequence { | |
42 | char *description; | |
43 | u32 init_flag; | |
44 | u32 done_flag; | |
45 | u32 err_flag; | |
46 | }; | |
47 | ||
48 | static struct ddrphy_init_sequence init_sequence[] = { | |
49 | { | |
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 | ||
99 | int 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) { | |
119 | #ifndef CONFIG_SPL_BUILD | |
120 | printf("%s: error: timeout during DDR training\n", | |
121 | __func__); | |
122 | #endif | |
123 | return -1; | |
124 | } | |
125 | udelay(1); | |
126 | pgsr0 = readl(&phy->pgsr[0]); | |
127 | } while ((pgsr0 & done_flag) != done_flag); | |
128 | ||
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); | |
134 | #endif | |
135 | return -1; | |
136 | } | |
137 | } | |
138 | ||
139 | #ifdef DISPLAY_ELAPSED_TIME | |
140 | printf("%s: info: elapsed time %ld msec\n", get_timer(start)); | |
141 | #endif | |
142 | ||
143 | return 0; | |
144 | } |