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/io.h>
9 #include <mach/ddrphy-regs.h>
10 
ddrphy_prepare_training(struct ddrphy __iomem * phy,int rank)11 void ddrphy_prepare_training(struct ddrphy __iomem *phy, int rank)
12 {
13 	int dx;
14 	u32 __iomem tmp, *p;
15 
16 	for (dx = 0; dx < NR_DATX8_PER_DDRPHY; dx++) {
17 		p = &phy->dx[dx].gcr;
18 
19 		tmp = readl(p);
20 		/* Specify the rank that should be write leveled */
21 		tmp &= ~DXGCR_WLRKEN_MASK;
22 		tmp |= (1 << (DXGCR_WLRKEN_SHIFT + rank)) & DXGCR_WLRKEN_MASK;
23 		writel(tmp, p);
24 	}
25 
26 	p = &phy->dtcr;
27 
28 	tmp = readl(p);
29 	/* Specify the rank used during data bit deskew and eye centering */
30 	tmp &= ~DTCR_DTRANK_MASK;
31 	tmp |= (rank << DTCR_DTRANK_SHIFT) & DTCR_DTRANK_MASK;
32 	/* Use Multi-Purpose Register for DQS gate training */
33 	tmp |= DTCR_DTMPR;
34 	/* Specify the rank enabled for data-training */
35 	tmp &= ~DTCR_RNKEN_MASK;
36 	tmp |= (1 << (DTCR_RNKEN_SHIFT + rank)) & DTCR_RNKEN_MASK;
37 	writel(tmp, p);
38 }
39 
40 struct ddrphy_init_sequence {
41 	char *description;
42 	u32 init_flag;
43 	u32 done_flag;
44 	u32 err_flag;
45 };
46 
47 static struct ddrphy_init_sequence init_sequence[] = {
48 	{
49 		"DRAM Initialization",
50 		PIR_DRAMRST | PIR_DRAMINIT,
51 		PGSR0_DIDONE,
52 		PGSR0_DIERR
53 	},
54 	{
55 		"Write Leveling",
56 		PIR_WL,
57 		PGSR0_WLDONE,
58 		PGSR0_WLERR
59 	},
60 	{
61 		"Read DQS Gate Training",
62 		PIR_QSGATE,
63 		PGSR0_QSGDONE,
64 		PGSR0_QSGERR
65 	},
66 	{
67 		"Write Leveling Adjustment",
68 		PIR_WLADJ,
69 		PGSR0_WLADONE,
70 		PGSR0_WLAERR
71 	},
72 	{
73 		"Read Bit Deskew",
74 		PIR_RDDSKW,
75 		PGSR0_RDDONE,
76 		PGSR0_RDERR
77 	},
78 	{
79 		"Write Bit Deskew",
80 		PIR_WRDSKW,
81 		PGSR0_WDDONE,
82 		PGSR0_WDERR
83 	},
84 	{
85 		"Read Eye Training",
86 		PIR_RDEYE,
87 		PGSR0_REDONE,
88 		PGSR0_REERR
89 	},
90 	{
91 		"Write Eye Training",
92 		PIR_WREYE,
93 		PGSR0_WEDONE,
94 		PGSR0_WEERR
95 	}
96 };
97 
ddrphy_training(struct ddrphy __iomem * phy)98 int ddrphy_training(struct ddrphy __iomem *phy)
99 {
100 	int i;
101 	u32 pgsr0;
102 	u32 init_flag = PIR_INIT;
103 	u32 done_flag = PGSR0_IDONE;
104 	int timeout = 50000; /* 50 msec is long enough */
105 #ifdef DISPLAY_ELAPSED_TIME
106 	ulong start = get_timer(0);
107 #endif
108 
109 	for (i = 0; i < ARRAY_SIZE(init_sequence); i++) {
110 		init_flag |= init_sequence[i].init_flag;
111 		done_flag |= init_sequence[i].done_flag;
112 	}
113 
114 	writel(init_flag, &phy->pir);
115 
116 	do {
117 		if (--timeout < 0) {
118 			printf("%s: error: timeout during DDR training\n",
119 								__func__);
120 			return -1;
121 		}
122 		udelay(1);
123 		pgsr0 = readl(&phy->pgsr[0]);
124 	} while ((pgsr0 & done_flag) != done_flag);
125 
126 	for (i = 0; i < ARRAY_SIZE(init_sequence); i++) {
127 		if (pgsr0 & init_sequence[i].err_flag) {
128 			printf("%s: error: %s failed\n", __func__,
129 						init_sequence[i].description);
130 			return -1;
131 		}
132 	}
133 
134 #ifdef DISPLAY_ELAPSED_TIME
135 	printf("%s: info: elapsed time %ld msec\n", get_timer(start));
136 #endif
137 
138 	return 0;
139 }
140