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 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 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 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 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 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