1 /*
2  * Copyright (C) 2011-2015 Masahiro Yamada <yamada.masahiro@socionext.com>
3  *
4  * SPDX-License-Identifier:	GPL-2.0+
5  */
6 
7 #include <common.h>
8 #include <linux/err.h>
9 #include <linux/io.h>
10 #include <mach/ddrphy-regs.h>
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_RANKEN_MASK;
37 	tmp |= (1 << (DTCR_RANKEN_SHIFT + rank)) & DTCR_RANKEN_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 const 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 			printf("%s: error: timeout during DDR training\n",
120 								__func__);
121 			return -ETIMEDOUT;
122 		}
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) {
129 			printf("%s: error: %s failed\n", __func__,
130 						init_sequence[i].description);
131 			return -EIO;
132 		}
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 }
141