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