1 // SPDX-License-Identifier: GPL-2.0+
2 /*
3 * Copyright (C) 2021 Rockchip Electronics Co., Ltd.
4 */
5
6 #if defined(CONFIG_ROCKCHIP_RV1126) || defined(CONFIG_ROCKCHIP_RK3568)
7
8 #include <common.h>
9 #include <console.h>
10 #include <asm/io.h>
11 #include <asm/arch-rockchip/sdram_common.h>
12 #if defined(CONFIG_ROCKCHIP_RV1126)
13 #include <asm/arch/sdram_rv1126.h>
14 #elif defined(CONFIG_ROCKCHIP_RK3568)
15 #include <asm/arch/sdram_rk3568.h>
16 #endif
17
18 #define __version__ "0.0.6"
19
20 #define PRINT_LENGTH 64
21 #ifndef PRINT_STEP
22 #define PRINT_STEP 1
23 #endif
24 #define PRINT_RANGE ((PRINT_LENGTH) * (PRINT_STEP))
25
26 struct print_border {
27 u16 far_left;
28 u16 far_right;
29 };
30
31 struct rw_trn_result result;
32
calc_print_border(struct cs_rw_trn_result * result,u8 byte_en,u16 deskew_num,struct print_border * print_border)33 static void calc_print_border(struct cs_rw_trn_result *result, u8 byte_en,
34 u16 deskew_num, struct print_border *print_border)
35 {
36 u16 far_left = deskew_num;
37 u16 far_right = 0;
38 u16 mid;
39 u8 dqs;
40 u8 dq;
41
42 if (deskew_num <= PRINT_RANGE) {
43 print_border->far_left = 0;
44 print_border->far_right = deskew_num - 1;
45
46 return;
47 }
48
49 for (dqs = 0; dqs < BYTE_NUM; dqs++) {
50 if ((byte_en & BIT(dqs)) == 0)
51 continue;
52
53 for (dq = 0; dq < 8; dq++) {
54 if (result->dqs[dqs].dq_min[dq] < far_left)
55 far_left = result->dqs[dqs].dq_min[dq];
56 if (result->dqs[dqs].dq_max[dq] > far_right)
57 far_right = result->dqs[dqs].dq_max[dq];
58 }
59 }
60
61 if (far_right - far_left + 1 > PRINT_RANGE) {
62 print_border->far_left = far_left & ~((u16)(PRINT_STEP * 4 - 1));
63 print_border->far_right = far_right | (PRINT_STEP * 4 - 1);
64 } else {
65 mid = (far_left + far_right) / 2;
66 if (mid < PRINT_RANGE / 2) {
67 print_border->far_left = 0;
68 print_border->far_right = PRINT_RANGE - 1;
69 } else if (mid > deskew_num - PRINT_RANGE / 2) {
70 print_border->far_left = deskew_num - PRINT_RANGE;
71 print_border->far_right = deskew_num - 1;
72 } else {
73 print_border->far_left = mid - PRINT_RANGE / 2;
74 print_border->far_right = mid + PRINT_RANGE / 2 - 1;
75 }
76 }
77 }
78
print_title_bar(struct print_border * print_border)79 static void print_title_bar(struct print_border *print_border)
80 {
81 int i;
82
83 printf(" ");
84 for (i = print_border->far_left; i < print_border->far_right;
85 i += PRINT_STEP * 4)
86 printf("%-4d", i);
87 printf(" Margin_L Sample Margin_R Width DQS\n");
88 }
89
print_ddr_dq_eye(struct fsp_rw_trn_result * fsp_result,u8 cs,u8 byte_en,u16 width_ref,struct print_border * print_border)90 static void print_ddr_dq_eye(struct fsp_rw_trn_result *fsp_result, u8 cs,
91 u8 byte_en, u16 width_ref,
92 struct print_border *print_border)
93 {
94 u16 sample;
95 u16 min;
96 u16 max;
97 u16 dq_eye_width;
98 u8 dqs;
99 u8 dq;
100 int i;
101 struct cs_rw_trn_result *result = &fsp_result->cs[cs];
102
103 for (dqs = 0; dqs < BYTE_NUM; dqs++) {
104 if ((byte_en & BIT(dqs)) == 0)
105 continue;
106
107 for (dq = 0; dq < 8; dq++) {
108 sample = fsp_result->min_val +
109 result->dqs[dqs].dq_deskew[dq];
110 min = result->dqs[dqs].dq_min[dq];
111 max = result->dqs[dqs].dq_max[dq];
112 dq_eye_width = max >= min ? max - min + 1 : 0;
113
114 printf("DQ%-2d ", dqs * 8 + dq);
115 for (i = print_border->far_left;
116 i <= print_border->far_right; i += PRINT_STEP) {
117 if (i / PRINT_STEP == sample / PRINT_STEP)
118 printf("|");
119 else if (i / PRINT_STEP >= min / PRINT_STEP &&
120 i / PRINT_STEP <= max / PRINT_STEP)
121 printf("*");
122 else
123 printf("-");
124 }
125
126 printf(" %5d%8d%8d",
127 sample > min ? sample - min : 0, sample,
128 max > sample ? max - sample : 0);
129 if (dq_eye_width >= width_ref)
130 printf("%8d%8d\n", dq_eye_width,
131 fsp_result->min_val +
132 result->dqs[dqs].dqs_deskew);
133 else
134 printf(" [%3d]%7d\n", dq_eye_width,
135 fsp_result->min_val +
136 result->dqs[dqs].dqs_deskew);
137 }
138 }
139 printf("\n");
140 }
141
cs_eye_width_min(struct cs_rw_trn_result * result,u8 byte_en,u16 deskew_num)142 static u16 cs_eye_width_min(struct cs_rw_trn_result *result, u8 byte_en,
143 u16 deskew_num)
144 {
145 u16 min;
146 u16 max;
147 u16 dq_eye_width;
148 u16 cs_eye_width = deskew_num;
149 u8 dqs;
150 u8 dq;
151
152 for (dqs = 0; dqs < BYTE_NUM; dqs++) {
153 if ((byte_en & BIT(dqs)) == 0)
154 continue;
155
156 for (dq = 0; dq < 8; dq++) {
157 min = result->dqs[dqs].dq_min[dq];
158 max = result->dqs[dqs].dq_max[dq];
159 dq_eye_width = max >= min ? max - min + 1 : 0;
160 if (cs_eye_width > dq_eye_width)
161 cs_eye_width = dq_eye_width;
162 }
163 }
164
165 return cs_eye_width;
166 }
167
do_ddr_dq_eye(cmd_tbl_t * cmdtp,int flag,int argc,char * const argv[])168 static int do_ddr_dq_eye(cmd_tbl_t *cmdtp, int flag, int argc,
169 char * const argv[])
170 {
171 unsigned long freq_mhz;
172
173 u32 ddr_type;
174 u16 rd_width = RD_DESKEW_NUM;
175 u16 wr_width = WR_DESKEW_NUM;
176 u16 cs_eye_width;
177 u16 rd_width_ref;
178 u16 wr_width_ref;
179 u16 width_ref_mhz;
180 u8 fsp = 0;
181 u8 cs;
182 int i;
183 struct print_border print_border;
184
185 printf("Rockchip DDR DQ Eye Tool v" __version__ "\n");
186
187 #if defined(CONFIG_ROCKCHIP_RV1126)
188 ddr_type = (readl(0xfe020208) >> 13) & 0x7;
189 #elif defined(CONFIG_ROCKCHIP_RK3568)
190 ddr_type = ((readl(0xfdc2020c) & (0x3 << 12)) >> 9) |
191 ((readl(0xfdc20208) >> 13) & 0x7);
192 #else
193 printf("Rockchip DDR DQ Eye Tool only support RK3568/RK3566 and RV1126 now.\n");
194 return CMD_RET_FAILURE;
195 #endif
196
197 if (readl(RW_TRN_RESULT_ADDR) == DDR_DQ_EYE_FLAG) {
198 memcpy(&result, (void *)(RW_TRN_RESULT_ADDR), sizeof(result));
199 } else {
200 printf("Fail to get data of DDR DQ eye.\n");
201 printf("Please update the Loader.\n");
202 return CMD_RET_FAILURE;
203 }
204
205 if (argc == 1) {
206 /* use the max freq if no arg */
207 for (i = 0; i < FSP_NUM; i++) {
208 if (result.fsp_mhz[i] > result.fsp_mhz[fsp])
209 fsp = i;
210 }
211 } else if (argc > 1) {
212 if (strict_strtoul(argv[1], 0, &freq_mhz) < 0)
213 return CMD_RET_USAGE;
214
215 if (freq_mhz >= 0 && freq_mhz < FSP_NUM) {
216 /* when user enter the fsp rather than the freq_mhz */
217 fsp = (u8)freq_mhz;
218 } else {
219 for (fsp = 0; fsp < FSP_NUM; fsp++)
220 if (result.fsp_mhz[fsp] == freq_mhz ||
221 result.fsp_mhz[fsp] == (u16)(freq_mhz / MHZ))
222 break;
223
224 if (fsp >= FSP_NUM)
225 return CMD_RET_USAGE;
226 }
227 } else {
228 return CMD_RET_FAILURE;
229 }
230
231 printf("DDR type: ");
232 switch (ddr_type) {
233 case LPDDR4X:
234 if (result.fsp_mhz[fsp] <
235 (LP4_WIDTH_REF_MHZ_L + LP4_WIDTH_REF_MHZ_H) / 2) {
236 rd_width_ref = LP4_RD_WIDTH_REF_L;
237 wr_width_ref = LP4_WR_WIDTH_REF_L;
238 width_ref_mhz = LP4_WIDTH_REF_MHZ_L;
239 } else {
240 rd_width_ref = LP4_RD_WIDTH_REF_H;
241 wr_width_ref = LP4_WR_WIDTH_REF_H;
242 width_ref_mhz = LP4_WIDTH_REF_MHZ_H;
243 }
244 printf("LPDDR4X");
245 break;
246 case LPDDR4:
247 if (result.fsp_mhz[fsp] <
248 (LP4_WIDTH_REF_MHZ_L + LP4_WIDTH_REF_MHZ_H) / 2) {
249 rd_width_ref = LP4_RD_WIDTH_REF_L;
250 wr_width_ref = LP4_WR_WIDTH_REF_L;
251 width_ref_mhz = LP4_WIDTH_REF_MHZ_L;
252 } else {
253 rd_width_ref = LP4_RD_WIDTH_REF_H;
254 wr_width_ref = LP4_WR_WIDTH_REF_H;
255 width_ref_mhz = LP4_WIDTH_REF_MHZ_H;
256 }
257 printf("LPDDR4");
258 break;
259 case LPDDR3:
260 if (result.fsp_mhz[fsp] <
261 (LP4_WIDTH_REF_MHZ_L + LP4_WIDTH_REF_MHZ_H) / 2) {
262 rd_width_ref = LP3_RD_WIDTH_REF_L;
263 wr_width_ref = LP3_WR_WIDTH_REF_L;
264 width_ref_mhz = LP3_WIDTH_REF_MHZ_L;
265 } else {
266 rd_width_ref = LP3_RD_WIDTH_REF_H;
267 wr_width_ref = LP3_WR_WIDTH_REF_H;
268 width_ref_mhz = LP3_WIDTH_REF_MHZ_H;
269 }
270 printf("LPDDR3");
271 break;
272 case DDR4:
273 if (result.fsp_mhz[fsp] <
274 (DDR4_WIDTH_REF_MHZ_L + DDR4_WIDTH_REF_MHZ_H) / 2) {
275 rd_width_ref = DDR4_RD_WIDTH_REF_L;
276 wr_width_ref = DDR4_WR_WIDTH_REF_L;
277 width_ref_mhz = DDR4_WIDTH_REF_MHZ_L;
278 } else {
279 rd_width_ref = DDR4_RD_WIDTH_REF_H;
280 wr_width_ref = DDR4_WR_WIDTH_REF_H;
281 width_ref_mhz = DDR4_WIDTH_REF_MHZ_H;
282 }
283 printf("DDR4");
284 break;
285 case DDR3:
286 default:
287 if (result.fsp_mhz[fsp] <
288 (DDR3_WIDTH_REF_MHZ_L + DDR3_WIDTH_REF_MHZ_H) / 2) {
289 rd_width_ref = DDR3_RD_WIDTH_REF_L;
290 wr_width_ref = DDR3_WR_WIDTH_REF_L;
291 width_ref_mhz = DDR3_WIDTH_REF_MHZ_L;
292 } else {
293 rd_width_ref = DDR3_RD_WIDTH_REF_H;
294 wr_width_ref = DDR3_WR_WIDTH_REF_H;
295 width_ref_mhz = DDR3_WIDTH_REF_MHZ_H;
296 }
297 printf("DDR3");
298 break;
299 } /* switch (ddr_type) */
300 printf("\n");
301
302 for (cs = 0; cs < result.cs_num; cs++) {
303 calc_print_border(&result.rd_fsp[fsp].cs[cs], result.byte_en,
304 RD_DESKEW_NUM, &print_border);
305 printf("CS%d %dMHz read DQ eye:\n", cs, result.fsp_mhz[fsp]);
306 print_title_bar(&print_border);
307 print_ddr_dq_eye(&result.rd_fsp[fsp], cs, result.byte_en,
308 rd_width_ref, &print_border);
309 cs_eye_width = cs_eye_width_min(&result.rd_fsp[fsp].cs[cs],
310 result.byte_en, RD_DESKEW_NUM);
311 if (rd_width > cs_eye_width)
312 rd_width = cs_eye_width;
313
314 printf("CS%d %dMHz write DQ eye:\n", cs, result.fsp_mhz[fsp]);
315 calc_print_border(&result.wr_fsp[fsp].cs[cs], result.byte_en,
316 WR_DESKEW_NUM, &print_border);
317 print_title_bar(&print_border);
318 print_ddr_dq_eye(&result.wr_fsp[fsp], cs, result.byte_en,
319 wr_width_ref, &print_border);
320 cs_eye_width = cs_eye_width_min(&result.wr_fsp[fsp].cs[cs],
321 result.byte_en, WR_DESKEW_NUM);
322 if (wr_width > cs_eye_width)
323 wr_width = cs_eye_width;
324 }
325 printf("DQ eye width min: %d(read), %d(write)\n", rd_width, wr_width);
326 printf("DQ eye width reference: %d(read), %d(write) in %dMHz\n",
327 rd_width_ref, wr_width_ref, width_ref_mhz);
328 if (rd_width < rd_width_ref || wr_width < wr_width_ref)
329 printf("ERROR: DQ eye width may be unreliable, please check!\n");
330
331 return CMD_RET_SUCCESS;
332 }
333
334 U_BOOT_CMD(ddr_dq_eye, 2, 1, do_ddr_dq_eye,
335 "Rockchip DDR DQ Eye Tool\n",
336 "arg1: DDR freq in MHz, null for the max freq.\n"
337 "example:\n"
338 " ddr_dq_eye 1056: show the DDR DQ eye in 1056MHz."
339 );
340
341 #endif /* if defined(CONFIG_ROCKCHIP_RV1126) || defined(CONFIG_ROCKCHIP_RK3568) */
342