blob: 8c2310ac906488c14ca5047e03b49c324b0e20a2 [file] [log] [blame]
Patrick Delaunay01a75102019-04-10 14:09:27 +02001// SPDX-License-Identifier: GPL-2.0+ OR BSD-3-Clause
2/*
3 * Copyright (C) 2019, STMicroelectronics - All Rights Reserved
4 */
5
Patrick Delaunay66b3b9d2020-11-06 19:01:36 +01006#define LOG_CATEGORY UCLASS_RAM
7
Patrick Delaunay01a75102019-04-10 14:09:27 +02008#include <common.h>
Simon Glass09140112020-05-10 11:40:03 -06009#include <command.h>
Patrick Delaunay01a75102019-04-10 14:09:27 +020010#include <console.h>
11#include <cli.h>
12#include <clk.h>
Simon Glassf7ae49f2020-05-10 11:40:05 -060013#include <log.h>
Patrick Delaunay01a75102019-04-10 14:09:27 +020014#include <malloc.h>
15#include <ram.h>
16#include <reset.h>
Simon Glass401d1c42020-10-30 21:38:53 -060017#include <asm/global_data.h>
Patrick Delaunay01a75102019-04-10 14:09:27 +020018#include "stm32mp1_ddr.h"
Patrick Delaunay0d447522019-04-10 14:09:28 +020019#include "stm32mp1_tests.h"
Patrick Delaunay01a75102019-04-10 14:09:27 +020020
21DECLARE_GLOBAL_DATA_PTR;
22
23enum ddr_command {
24 DDR_CMD_HELP,
25 DDR_CMD_INFO,
26 DDR_CMD_FREQ,
27 DDR_CMD_RESET,
28 DDR_CMD_PARAM,
29 DDR_CMD_PRINT,
30 DDR_CMD_EDIT,
31 DDR_CMD_STEP,
32 DDR_CMD_NEXT,
33 DDR_CMD_GO,
34 DDR_CMD_TEST,
35 DDR_CMD_TUNING,
36 DDR_CMD_UNKNOWN,
37};
38
39const char *step_str[] = {
40 [STEP_DDR_RESET] = "DDR_RESET",
41 [STEP_CTL_INIT] = "DDR_CTRL_INIT_DONE",
42 [STEP_PHY_INIT] = "DDR PHY_INIT_DONE",
43 [STEP_DDR_READY] = "DDR_READY",
44 [STEP_RUN] = "RUN"
45};
46
47enum ddr_command stm32mp1_get_command(char *cmd, int argc)
48{
49 const char *cmd_string[DDR_CMD_UNKNOWN] = {
50 [DDR_CMD_HELP] = "help",
51 [DDR_CMD_INFO] = "info",
52 [DDR_CMD_FREQ] = "freq",
53 [DDR_CMD_RESET] = "reset",
54 [DDR_CMD_PARAM] = "param",
55 [DDR_CMD_PRINT] = "print",
56 [DDR_CMD_EDIT] = "edit",
57 [DDR_CMD_STEP] = "step",
58 [DDR_CMD_NEXT] = "next",
59 [DDR_CMD_GO] = "go",
Patrick Delaunay0d447522019-04-10 14:09:28 +020060#ifdef CONFIG_STM32MP1_DDR_TESTS
61 [DDR_CMD_TEST] = "test",
62#endif
Patrick Delaunay187c41d2019-04-10 14:09:29 +020063#ifdef CONFIG_STM32MP1_DDR_TUNING
64 [DDR_CMD_TUNING] = "tuning",
65#endif
Patrick Delaunay01a75102019-04-10 14:09:27 +020066 };
67 /* min and max number of argument */
68 const char cmd_arg[DDR_CMD_UNKNOWN][2] = {
69 [DDR_CMD_HELP] = { 0, 0 },
70 [DDR_CMD_INFO] = { 0, 255 },
71 [DDR_CMD_FREQ] = { 0, 1 },
72 [DDR_CMD_RESET] = { 0, 0 },
73 [DDR_CMD_PARAM] = { 0, 2 },
74 [DDR_CMD_PRINT] = { 0, 1 },
75 [DDR_CMD_EDIT] = { 2, 2 },
76 [DDR_CMD_STEP] = { 0, 1 },
77 [DDR_CMD_NEXT] = { 0, 0 },
78 [DDR_CMD_GO] = { 0, 0 },
Patrick Delaunay0d447522019-04-10 14:09:28 +020079#ifdef CONFIG_STM32MP1_DDR_TESTS
80 [DDR_CMD_TEST] = { 0, 255 },
81#endif
Patrick Delaunay187c41d2019-04-10 14:09:29 +020082#ifdef CONFIG_STM32MP1_DDR_TUNING
83 [DDR_CMD_TUNING] = { 0, 255 },
84#endif
Patrick Delaunay01a75102019-04-10 14:09:27 +020085 };
86 int i;
87
88 for (i = 0; i < DDR_CMD_UNKNOWN; i++)
89 if (!strcmp(cmd, cmd_string[i])) {
90 if (argc - 1 < cmd_arg[i][0]) {
91 printf("no enought argument (min=%d)\n",
92 cmd_arg[i][0]);
93 return DDR_CMD_UNKNOWN;
94 } else if (argc - 1 > cmd_arg[i][1]) {
95 printf("too many argument (max=%d)\n",
96 cmd_arg[i][1]);
97 return DDR_CMD_UNKNOWN;
98 } else {
99 return i;
100 }
101 }
102
103 printf("unknown command %s\n", cmd);
104 return DDR_CMD_UNKNOWN;
105}
106
107static void stm32mp1_do_usage(void)
108{
109 const char *usage = {
110 "commands:\n\n"
111 "help displays help\n"
112 "info displays DDR information\n"
113 "info <param> <val> changes DDR information\n"
Patrick Delaunay9368bdf2020-03-06 11:14:11 +0100114 " with <param> = step, name, size, speed or cal\n"
Patrick Delaunay01a75102019-04-10 14:09:27 +0200115 "freq displays the DDR PHY frequency in kHz\n"
116 "freq <freq> changes the DDR PHY frequency\n"
117 "param [type|reg] prints input parameters\n"
118 "param <reg> <val> edits parameters in step 0\n"
119 "print [type|reg] dumps registers\n"
120 "edit <reg> <val> modifies one register\n"
121 "step lists the available step\n"
122 "step <n> go to the step <n>\n"
123 "next goes to the next step\n"
124 "go continues the U-Boot SPL execution\n"
125 "reset reboots machine\n"
Patrick Delaunay0d447522019-04-10 14:09:28 +0200126#ifdef CONFIG_STM32MP1_DDR_TESTS
127 "test [help] | <n> [...] lists (with help) or executes test <n>\n"
128#endif
Patrick Delaunay187c41d2019-04-10 14:09:29 +0200129#ifdef CONFIG_STM32MP1_DDR_TUNING
130 "tuning [help] | <n> [...] lists (with help) or execute tuning <n>\n"
131#endif
Patrick Delaunay01a75102019-04-10 14:09:27 +0200132 "\nwith for [type|reg]:\n"
133 " all registers if absent\n"
134 " <type> = ctl, phy\n"
135 " or one category (static, timing, map, perf, cal, dyn)\n"
136 " <reg> = name of the register\n"
137 };
138
139 puts(usage);
140}
141
142static bool stm32mp1_check_step(enum stm32mp1_ddr_interact_step step,
143 enum stm32mp1_ddr_interact_step expected)
144{
145 if (step != expected) {
146 printf("invalid step %d:%s expecting %d:%s\n",
147 step, step_str[step],
148 expected,
149 step_str[expected]);
150 return false;
151 }
152 return true;
153}
154
155static void stm32mp1_do_info(struct ddr_info *priv,
156 struct stm32mp1_ddr_config *config,
157 enum stm32mp1_ddr_interact_step step,
Simon Glass09140112020-05-10 11:40:03 -0600158 int argc, char *const argv[])
Patrick Delaunay01a75102019-04-10 14:09:27 +0200159{
160 unsigned long value;
161 static char *ddr_name;
162
163 if (argc == 1) {
164 printf("step = %d : %s\n", step, step_str[step]);
165 printf("name = %s\n", config->info.name);
166 printf("size = 0x%x\n", config->info.size);
167 printf("speed = %d kHz\n", config->info.speed);
Patrick Delaunay9368bdf2020-03-06 11:14:11 +0100168 printf("cal = %d\n", config->p_cal_present);
Patrick Delaunay01a75102019-04-10 14:09:27 +0200169 return;
170 }
171
172 if (argc < 3) {
173 printf("no enought parameter\n");
174 return;
175 }
176 if (!strcmp(argv[1], "name")) {
177 u32 i, name_len = 0;
178
179 for (i = 2; i < argc; i++)
180 name_len += strlen(argv[i]) + 1;
181 if (ddr_name)
182 free(ddr_name);
183 ddr_name = malloc(name_len);
184 config->info.name = ddr_name;
185 if (!ddr_name) {
186 printf("alloc error, length %d\n", name_len);
187 return;
188 }
189 strcpy(ddr_name, argv[2]);
190 for (i = 3; i < argc; i++) {
191 strcat(ddr_name, " ");
192 strcat(ddr_name, argv[i]);
193 }
194 printf("name = %s\n", ddr_name);
195 return;
196 }
197 if (!strcmp(argv[1], "size")) {
198 if (strict_strtoul(argv[2], 16, &value) < 0) {
199 printf("invalid value %s\n", argv[2]);
200 } else {
201 config->info.size = value;
202 printf("size = 0x%x\n", config->info.size);
203 }
204 return;
205 }
206 if (!strcmp(argv[1], "speed")) {
207 if (strict_strtoul(argv[2], 10, &value) < 0) {
208 printf("invalid value %s\n", argv[2]);
209 } else {
210 config->info.speed = value;
211 printf("speed = %d kHz\n", config->info.speed);
212 value = clk_get_rate(&priv->clk);
213 printf("DDRPHY = %ld kHz\n", value / 1000);
214 }
215 return;
216 }
Patrick Delaunay9368bdf2020-03-06 11:14:11 +0100217 if (!strcmp(argv[1], "cal")) {
218 if (strict_strtoul(argv[2], 10, &value) < 0 ||
219 (value != 0 && value != 1)) {
220 printf("invalid value %s\n", argv[2]);
221 } else {
222 config->p_cal_present = value;
223 printf("cal = %d\n", config->p_cal_present);
224 }
225 return;
226 }
Patrick Delaunay01a75102019-04-10 14:09:27 +0200227 printf("argument %s invalid\n", argv[1]);
228}
229
230static bool stm32mp1_do_freq(struct ddr_info *priv,
Simon Glass09140112020-05-10 11:40:03 -0600231 int argc, char *const argv[])
Patrick Delaunay01a75102019-04-10 14:09:27 +0200232{
233 unsigned long ddrphy_clk;
234
235 if (argc == 2) {
236 if (strict_strtoul(argv[1], 0, &ddrphy_clk) < 0) {
237 printf("invalid argument %s", argv[1]);
238 return false;
239 }
240 if (clk_set_rate(&priv->clk, ddrphy_clk * 1000)) {
241 printf("ERROR: update failed!\n");
242 return false;
243 }
244 }
245 ddrphy_clk = clk_get_rate(&priv->clk);
246 printf("DDRPHY = %ld kHz\n", ddrphy_clk / 1000);
247 if (argc == 2)
248 return true;
249 return false;
250}
251
252static void stm32mp1_do_param(enum stm32mp1_ddr_interact_step step,
253 const struct stm32mp1_ddr_config *config,
Simon Glass09140112020-05-10 11:40:03 -0600254 int argc, char *const argv[])
Patrick Delaunay01a75102019-04-10 14:09:27 +0200255{
256 switch (argc) {
257 case 1:
258 stm32mp1_dump_param(config, NULL);
259 break;
260 case 2:
261 if (stm32mp1_dump_param(config, argv[1]))
262 printf("invalid argument %s\n",
263 argv[1]);
264 break;
265 case 3:
266 if (!stm32mp1_check_step(step, STEP_DDR_RESET))
267 return;
268 stm32mp1_edit_param(config, argv[1], argv[2]);
269 break;
270 }
271}
272
273static void stm32mp1_do_print(struct ddr_info *priv,
Simon Glass09140112020-05-10 11:40:03 -0600274 int argc, char *const argv[])
Patrick Delaunay01a75102019-04-10 14:09:27 +0200275{
276 switch (argc) {
277 case 1:
278 stm32mp1_dump_reg(priv, NULL);
279 break;
280 case 2:
281 if (stm32mp1_dump_reg(priv, argv[1]))
282 printf("invalid argument %s\n",
283 argv[1]);
284 break;
285 }
286}
287
288static int stm32mp1_do_step(enum stm32mp1_ddr_interact_step step,
Simon Glass09140112020-05-10 11:40:03 -0600289 int argc, char *const argv[])
Patrick Delaunay01a75102019-04-10 14:09:27 +0200290{
291 int i;
292 unsigned long value;
293
294 switch (argc) {
295 case 1:
296 for (i = 0; i < ARRAY_SIZE(step_str); i++)
297 printf("%d:%s\n", i, step_str[i]);
298 break;
299
300 case 2:
301 if ((strict_strtoul(argv[1], 0,
302 &value) < 0) ||
303 value >= ARRAY_SIZE(step_str)) {
304 printf("invalid argument %s\n",
305 argv[1]);
306 goto end;
307 }
308
309 if (value != STEP_DDR_RESET &&
310 value <= step) {
311 printf("invalid target %d:%s, current step is %d:%s\n",
312 (int)value, step_str[value],
313 step, step_str[step]);
314 goto end;
315 }
316 printf("step to %d:%s\n",
317 (int)value, step_str[value]);
318 return (int)value;
319 };
320
321end:
322 return step;
323}
324
Patrick Delaunay187c41d2019-04-10 14:09:29 +0200325#if defined(CONFIG_STM32MP1_DDR_TESTS) || defined(CONFIG_STM32MP1_DDR_TUNING)
Patrick Delaunay0d447522019-04-10 14:09:28 +0200326static const char * const s_result[] = {
327 [TEST_PASSED] = "Pass",
328 [TEST_FAILED] = "Failed",
329 [TEST_ERROR] = "Error"
330};
331
332static void stm32mp1_ddr_subcmd(struct ddr_info *priv,
333 int argc, char *argv[],
334 const struct test_desc array[],
335 const int array_nb)
336{
337 int i;
338 unsigned long value;
339 int result;
340 char string[50] = "";
341
342 if (argc == 1) {
343 printf("%s:%d\n", argv[0], array_nb);
344 for (i = 0; i < array_nb; i++)
345 printf("%d:%s:%s\n",
346 i, array[i].name, array[i].usage);
347 return;
348 }
349 if (argc > 1 && !strcmp(argv[1], "help")) {
350 printf("%s:%d\n", argv[0], array_nb);
351 for (i = 0; i < array_nb; i++)
352 printf("%d:%s:%s:%s\n", i,
353 array[i].name, array[i].usage, array[i].help);
354 return;
355 }
356
357 if ((strict_strtoul(argv[1], 0, &value) < 0) ||
358 value >= array_nb) {
359 sprintf(string, "invalid argument %s",
360 argv[1]);
361 result = TEST_FAILED;
362 goto end;
363 }
364
365 if (argc > (array[value].max_args + 2)) {
366 sprintf(string, "invalid nb of args %d, max %d",
367 argc - 2, array[value].max_args);
368 result = TEST_FAILED;
369 goto end;
370 }
371
372 printf("execute %d:%s\n", (int)value, array[value].name);
373 clear_ctrlc();
374 result = array[value].fct(priv->ctl, priv->phy,
375 string, argc - 2, &argv[2]);
376
377end:
378 printf("Result: %s [%s]\n", s_result[result], string);
379}
380#endif
381
Patrick Delaunay01a75102019-04-10 14:09:27 +0200382bool stm32mp1_ddr_interactive(void *priv,
383 enum stm32mp1_ddr_interact_step step,
384 const struct stm32mp1_ddr_config *config)
385{
Patrick Delaunay01a75102019-04-10 14:09:27 +0200386 char buffer[CONFIG_SYS_CBSIZE];
387 char *argv[CONFIG_SYS_MAXARGS + 1]; /* NULL terminated */
388 int argc;
389 static int next_step = -1;
390
391 if (next_step < 0 && step == STEP_DDR_RESET) {
392#ifdef CONFIG_STM32MP1_DDR_INTERACTIVE_FORCE
393 gd->flags &= ~(GD_FLG_SILENT |
394 GD_FLG_DISABLE_CONSOLE);
395 next_step = STEP_DDR_RESET;
396#else
397 unsigned long start = get_timer(0);
398
399 while (1) {
Heinrich Schuchardtc670aee2020-10-07 18:11:48 +0200400 if (tstc() && (getchar() == 'd')) {
Patrick Delaunay01a75102019-04-10 14:09:27 +0200401 next_step = STEP_DDR_RESET;
402 break;
403 }
404 if (get_timer(start) > 100)
405 break;
406 }
407#endif
408 }
409
Patrick Delaunay66b3b9d2020-11-06 19:01:36 +0100410 log_debug("** step %d ** %s / %d\n", step, step_str[step], next_step);
Patrick Delaunay01a75102019-04-10 14:09:27 +0200411
412 if (next_step < 0)
413 return false;
414
415 if (step < 0 || step > ARRAY_SIZE(step_str)) {
416 printf("** step %d ** INVALID\n", step);
417 return false;
418 }
419
420 printf("%d:%s\n", step, step_str[step]);
Patrick Delaunay01a75102019-04-10 14:09:27 +0200421
422 if (next_step > step)
423 return false;
424
425 while (next_step == step) {
Patrick Delaunay1c55a912020-03-06 11:14:05 +0100426 cli_readline_into_buffer("DDR>", buffer, 0);
Patrick Delaunay01a75102019-04-10 14:09:27 +0200427 argc = cli_simple_parse_line(buffer, argv);
428 if (!argc)
429 continue;
430
431 switch (stm32mp1_get_command(argv[0], argc)) {
432 case DDR_CMD_HELP:
433 stm32mp1_do_usage();
434 break;
435
436 case DDR_CMD_INFO:
437 stm32mp1_do_info(priv,
438 (struct stm32mp1_ddr_config *)config,
439 step, argc, argv);
440 break;
441
442 case DDR_CMD_FREQ:
443 if (stm32mp1_do_freq(priv, argc, argv))
444 next_step = STEP_DDR_RESET;
445 break;
446
447 case DDR_CMD_RESET:
448 do_reset(NULL, 0, 0, NULL);
449 break;
450
451 case DDR_CMD_PARAM:
452 stm32mp1_do_param(step, config, argc, argv);
453 break;
454
455 case DDR_CMD_PRINT:
456 stm32mp1_do_print(priv, argc, argv);
457 break;
458
459 case DDR_CMD_EDIT:
460 stm32mp1_edit_reg(priv, argv[1], argv[2]);
461 break;
462
463 case DDR_CMD_GO:
464 next_step = STEP_RUN;
465 break;
466
467 case DDR_CMD_NEXT:
468 next_step = step + 1;
469 break;
470
471 case DDR_CMD_STEP:
472 next_step = stm32mp1_do_step(step, argc, argv);
473 break;
474
Patrick Delaunay0d447522019-04-10 14:09:28 +0200475#ifdef CONFIG_STM32MP1_DDR_TESTS
476 case DDR_CMD_TEST:
477 if (!stm32mp1_check_step(step, STEP_DDR_READY))
478 continue;
479 stm32mp1_ddr_subcmd(priv, argc, argv, test, test_nb);
480 break;
481#endif
482
Patrick Delaunay187c41d2019-04-10 14:09:29 +0200483#ifdef CONFIG_STM32MP1_DDR_TUNING
484 case DDR_CMD_TUNING:
485 if (!stm32mp1_check_step(step, STEP_DDR_READY))
486 continue;
487 stm32mp1_ddr_subcmd(priv, argc, argv,
488 tuning, tuning_nb);
489 break;
490#endif
491
Patrick Delaunay01a75102019-04-10 14:09:27 +0200492 default:
493 break;
494 }
495 }
496 return next_step == STEP_DDR_RESET;
497}