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