xref: /rk3399_rockchip-uboot/cmd/ddr_tool/ddr_dq_eye/ddr_dq_eye.c (revision f9a1c31d51021ad79fb5d72e074600eeeeee80f8)
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