1 /*
2  * Copyright (C) 2011-2014 Panasonic Corporation
3  * Copyright (C) 2015-2016 Socionext Inc.
4  *
5  * SPDX-License-Identifier:	GPL-2.0+
6  */
7 
8 #include <common.h>
9 #include <linux/err.h>
10 #include <linux/io.h>
11 
12 #include "ddrphy-init.h"
13 #include "ddrphy-regs.h"
14 
15 /* for LD4, Pro4, sLD8 */
16 #define NR_DATX8_PER_DDRPHY	2
17 
18 void ddrphy_prepare_training(void __iomem *phy_base, int rank)
19 {
20 	void __iomem *dx_base = phy_base + PHY_DX_BASE;
21 	int dx;
22 	u32 tmp;
23 
24 	for (dx = 0; dx < NR_DATX8_PER_DDRPHY; dx++) {
25 		tmp = readl(dx_base + PHY_DX_GCR);
26 		/* Specify the rank that should be write leveled */
27 		tmp &= ~PHY_DX_GCR_WLRKEN_MASK;
28 		tmp |= (1 << (PHY_DX_GCR_WLRKEN_SHIFT + rank)) &
29 			PHY_DX_GCR_WLRKEN_MASK;
30 		writel(tmp, dx_base + PHY_DX_GCR);
31 		dx_base += PHY_DX_STRIDE;
32 	}
33 
34 	tmp = readl(phy_base + PHY_DTCR);
35 	/* Specify the rank used during data bit deskew and eye centering */
36 	tmp &= ~PHY_DTCR_DTRANK_MASK;
37 	tmp |= (rank << PHY_DTCR_DTRANK_SHIFT) & PHY_DTCR_DTRANK_MASK;
38 	/* Use Multi-Purpose Register for DQS gate training */
39 	tmp |= PHY_DTCR_DTMPR;
40 	/* Specify the rank enabled for data-training */
41 	tmp &= ~PHY_DTCR_RANKEN_MASK;
42 	tmp |= (1 << (PHY_DTCR_RANKEN_SHIFT + rank)) & PHY_DTCR_RANKEN_MASK;
43 	writel(tmp, phy_base + PHY_DTCR);
44 }
45 
46 struct ddrphy_init_sequence {
47 	char *description;
48 	u32 init_flag;
49 	u32 done_flag;
50 	u32 err_flag;
51 };
52 
53 static const struct ddrphy_init_sequence init_sequence[] = {
54 	{
55 		"DRAM Initialization",
56 		PHY_PIR_DRAMRST | PHY_PIR_DRAMINIT,
57 		PHY_PGSR0_DIDONE,
58 		PHY_PGSR0_DIERR
59 	},
60 	{
61 		"Write Leveling",
62 		PHY_PIR_WL,
63 		PHY_PGSR0_WLDONE,
64 		PHY_PGSR0_WLERR
65 	},
66 	{
67 		"Read DQS Gate Training",
68 		PHY_PIR_QSGATE,
69 		PHY_PGSR0_QSGDONE,
70 		PHY_PGSR0_QSGERR
71 	},
72 	{
73 		"Write Leveling Adjustment",
74 		PHY_PIR_WLADJ,
75 		PHY_PGSR0_WLADONE,
76 		PHY_PGSR0_WLAERR
77 	},
78 	{
79 		"Read Bit Deskew",
80 		PHY_PIR_RDDSKW,
81 		PHY_PGSR0_RDDONE,
82 		PHY_PGSR0_RDERR
83 	},
84 	{
85 		"Write Bit Deskew",
86 		PHY_PIR_WRDSKW,
87 		PHY_PGSR0_WDDONE,
88 		PHY_PGSR0_WDERR
89 	},
90 	{
91 		"Read Eye Training",
92 		PHY_PIR_RDEYE,
93 		PHY_PGSR0_REDONE,
94 		PHY_PGSR0_REERR
95 	},
96 	{
97 		"Write Eye Training",
98 		PHY_PIR_WREYE,
99 		PHY_PGSR0_WEDONE,
100 		PHY_PGSR0_WEERR
101 	}
102 };
103 
104 int ddrphy_training(void __iomem *phy_base)
105 {
106 	int i;
107 	u32 pgsr0;
108 	u32 init_flag = PHY_PIR_INIT;
109 	u32 done_flag = PHY_PGSR0_IDONE;
110 	int timeout = 50000; /* 50 msec is long enough */
111 #ifdef DISPLAY_ELAPSED_TIME
112 	ulong start = get_timer(0);
113 #endif
114 
115 	for (i = 0; i < ARRAY_SIZE(init_sequence); i++) {
116 		init_flag |= init_sequence[i].init_flag;
117 		done_flag |= init_sequence[i].done_flag;
118 	}
119 
120 	writel(init_flag, phy_base + PHY_PIR);
121 
122 	do {
123 		if (--timeout < 0) {
124 			printf("%s: error: timeout during DDR training\n",
125 								__func__);
126 			return -ETIMEDOUT;
127 		}
128 		udelay(1);
129 		pgsr0 = readl(phy_base + PHY_PGSR0);
130 	} while ((pgsr0 & done_flag) != done_flag);
131 
132 	for (i = 0; i < ARRAY_SIZE(init_sequence); i++) {
133 		if (pgsr0 & init_sequence[i].err_flag) {
134 			printf("%s: error: %s failed\n", __func__,
135 						init_sequence[i].description);
136 			return -EIO;
137 		}
138 	}
139 
140 #ifdef DISPLAY_ELAPSED_TIME
141 	printf("%s: info: elapsed time %ld msec\n", get_timer(start));
142 #endif
143 
144 	return 0;
145 }
146