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 
11 #include "ddrphy-regs.h"
12 
13 void 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 */
37 	tmp &= ~DTCR_RANKEN_MASK;
38 	tmp |= (1 << (DTCR_RANKEN_SHIFT + rank)) & DTCR_RANKEN_MASK;
39 	writel(tmp, p);
40 }
41 
42 struct ddrphy_init_sequence {
43 	char *description;
44 	u32 init_flag;
45 	u32 done_flag;
46 	u32 err_flag;
47 };
48 
49 static const struct ddrphy_init_sequence init_sequence[] = {
50 	{
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 
100 int 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) {
120 			printf("%s: error: timeout during DDR training\n",
121 								__func__);
122 			return -ETIMEDOUT;
123 		}
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) {
130 			printf("%s: error: %s failed\n", __func__,
131 						init_sequence[i].description);
132 			return -EIO;
133 		}
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 }
142