blob: e45a2489c50856cfda05d540c34108a54250b1da [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>
17#include "stm32mp1_ddr.h"
Patrick Delaunay0d447522019-04-10 14:09:28 +020018#include "stm32mp1_tests.h"
Patrick Delaunay01a75102019-04-10 14:09:27 +020019
20DECLARE_GLOBAL_DATA_PTR;
21
22enum ddr_command {
23 DDR_CMD_HELP,
24 DDR_CMD_INFO,
25 DDR_CMD_FREQ,
26 DDR_CMD_RESET,
27 DDR_CMD_PARAM,
28 DDR_CMD_PRINT,
29 DDR_CMD_EDIT,
30 DDR_CMD_STEP,
31 DDR_CMD_NEXT,
32 DDR_CMD_GO,
33 DDR_CMD_TEST,
34 DDR_CMD_TUNING,
35 DDR_CMD_UNKNOWN,
36};
37
38const char *step_str[] = {
39 [STEP_DDR_RESET] = "DDR_RESET",
40 [STEP_CTL_INIT] = "DDR_CTRL_INIT_DONE",
41 [STEP_PHY_INIT] = "DDR PHY_INIT_DONE",
42 [STEP_DDR_READY] = "DDR_READY",
43 [STEP_RUN] = "RUN"
44};
45
46enum ddr_command stm32mp1_get_command(char *cmd, int argc)
47{
48 const char *cmd_string[DDR_CMD_UNKNOWN] = {
49 [DDR_CMD_HELP] = "help",
50 [DDR_CMD_INFO] = "info",
51 [DDR_CMD_FREQ] = "freq",
52 [DDR_CMD_RESET] = "reset",
53 [DDR_CMD_PARAM] = "param",
54 [DDR_CMD_PRINT] = "print",
55 [DDR_CMD_EDIT] = "edit",
56 [DDR_CMD_STEP] = "step",
57 [DDR_CMD_NEXT] = "next",
58 [DDR_CMD_GO] = "go",
Patrick Delaunay0d447522019-04-10 14:09:28 +020059#ifdef CONFIG_STM32MP1_DDR_TESTS
60 [DDR_CMD_TEST] = "test",
61#endif
Patrick Delaunay187c41d2019-04-10 14:09:29 +020062#ifdef CONFIG_STM32MP1_DDR_TUNING
63 [DDR_CMD_TUNING] = "tuning",
64#endif
Patrick Delaunay01a75102019-04-10 14:09:27 +020065 };
66 /* min and max number of argument */
67 const char cmd_arg[DDR_CMD_UNKNOWN][2] = {
68 [DDR_CMD_HELP] = { 0, 0 },
69 [DDR_CMD_INFO] = { 0, 255 },
70 [DDR_CMD_FREQ] = { 0, 1 },
71 [DDR_CMD_RESET] = { 0, 0 },
72 [DDR_CMD_PARAM] = { 0, 2 },
73 [DDR_CMD_PRINT] = { 0, 1 },
74 [DDR_CMD_EDIT] = { 2, 2 },
75 [DDR_CMD_STEP] = { 0, 1 },
76 [DDR_CMD_NEXT] = { 0, 0 },
77 [DDR_CMD_GO] = { 0, 0 },
Patrick Delaunay0d447522019-04-10 14:09:28 +020078#ifdef CONFIG_STM32MP1_DDR_TESTS
79 [DDR_CMD_TEST] = { 0, 255 },
80#endif
Patrick Delaunay187c41d2019-04-10 14:09:29 +020081#ifdef CONFIG_STM32MP1_DDR_TUNING
82 [DDR_CMD_TUNING] = { 0, 255 },
83#endif
Patrick Delaunay01a75102019-04-10 14:09:27 +020084 };
85 int i;
86
87 for (i = 0; i < DDR_CMD_UNKNOWN; i++)
88 if (!strcmp(cmd, cmd_string[i])) {
89 if (argc - 1 < cmd_arg[i][0]) {
90 printf("no enought argument (min=%d)\n",
91 cmd_arg[i][0]);
92 return DDR_CMD_UNKNOWN;
93 } else if (argc - 1 > cmd_arg[i][1]) {
94 printf("too many argument (max=%d)\n",
95 cmd_arg[i][1]);
96 return DDR_CMD_UNKNOWN;
97 } else {
98 return i;
99 }
100 }
101
102 printf("unknown command %s\n", cmd);
103 return DDR_CMD_UNKNOWN;
104}
105
106static void stm32mp1_do_usage(void)
107{
108 const char *usage = {
109 "commands:\n\n"
110 "help displays help\n"
111 "info displays DDR information\n"
112 "info <param> <val> changes DDR information\n"
Patrick Delaunay9368bdf2020-03-06 11:14:11 +0100113 " with <param> = step, name, size, speed or cal\n"
Patrick Delaunay01a75102019-04-10 14:09:27 +0200114 "freq displays the DDR PHY frequency in kHz\n"
115 "freq <freq> changes the DDR PHY frequency\n"
116 "param [type|reg] prints input parameters\n"
117 "param <reg> <val> edits parameters in step 0\n"
118 "print [type|reg] dumps registers\n"
119 "edit <reg> <val> modifies one register\n"
120 "step lists the available step\n"
121 "step <n> go to the step <n>\n"
122 "next goes to the next step\n"
123 "go continues the U-Boot SPL execution\n"
124 "reset reboots machine\n"
Patrick Delaunay0d447522019-04-10 14:09:28 +0200125#ifdef CONFIG_STM32MP1_DDR_TESTS
126 "test [help] | <n> [...] lists (with help) or executes test <n>\n"
127#endif
Patrick Delaunay187c41d2019-04-10 14:09:29 +0200128#ifdef CONFIG_STM32MP1_DDR_TUNING
129 "tuning [help] | <n> [...] lists (with help) or execute tuning <n>\n"
130#endif
Patrick Delaunay01a75102019-04-10 14:09:27 +0200131 "\nwith for [type|reg]:\n"
132 " all registers if absent\n"
133 " <type> = ctl, phy\n"
134 " or one category (static, timing, map, perf, cal, dyn)\n"
135 " <reg> = name of the register\n"
136 };
137
138 puts(usage);
139}
140
141static bool stm32mp1_check_step(enum stm32mp1_ddr_interact_step step,
142 enum stm32mp1_ddr_interact_step expected)
143{
144 if (step != expected) {
145 printf("invalid step %d:%s expecting %d:%s\n",
146 step, step_str[step],
147 expected,
148 step_str[expected]);
149 return false;
150 }
151 return true;
152}
153
154static void stm32mp1_do_info(struct ddr_info *priv,
155 struct stm32mp1_ddr_config *config,
156 enum stm32mp1_ddr_interact_step step,
Simon Glass09140112020-05-10 11:40:03 -0600157 int argc, char *const argv[])
Patrick Delaunay01a75102019-04-10 14:09:27 +0200158{
159 unsigned long value;
160 static char *ddr_name;
161
162 if (argc == 1) {
163 printf("step = %d : %s\n", step, step_str[step]);
164 printf("name = %s\n", config->info.name);
165 printf("size = 0x%x\n", config->info.size);
166 printf("speed = %d kHz\n", config->info.speed);
Patrick Delaunay9368bdf2020-03-06 11:14:11 +0100167 printf("cal = %d\n", config->p_cal_present);
Patrick Delaunay01a75102019-04-10 14:09:27 +0200168 return;
169 }
170
171 if (argc < 3) {
172 printf("no enought parameter\n");
173 return;
174 }
175 if (!strcmp(argv[1], "name")) {
176 u32 i, name_len = 0;
177
178 for (i = 2; i < argc; i++)
179 name_len += strlen(argv[i]) + 1;
180 if (ddr_name)
181 free(ddr_name);
182 ddr_name = malloc(name_len);
183 config->info.name = ddr_name;
184 if (!ddr_name) {
185 printf("alloc error, length %d\n", name_len);
186 return;
187 }
188 strcpy(ddr_name, argv[2]);
189 for (i = 3; i < argc; i++) {
190 strcat(ddr_name, " ");
191 strcat(ddr_name, argv[i]);
192 }
193 printf("name = %s\n", ddr_name);
194 return;
195 }
196 if (!strcmp(argv[1], "size")) {
197 if (strict_strtoul(argv[2], 16, &value) < 0) {
198 printf("invalid value %s\n", argv[2]);
199 } else {
200 config->info.size = value;
201 printf("size = 0x%x\n", config->info.size);
202 }
203 return;
204 }
205 if (!strcmp(argv[1], "speed")) {
206 if (strict_strtoul(argv[2], 10, &value) < 0) {
207 printf("invalid value %s\n", argv[2]);
208 } else {
209 config->info.speed = value;
210 printf("speed = %d kHz\n", config->info.speed);
211 value = clk_get_rate(&priv->clk);
212 printf("DDRPHY = %ld kHz\n", value / 1000);
213 }
214 return;
215 }
Patrick Delaunay9368bdf2020-03-06 11:14:11 +0100216 if (!strcmp(argv[1], "cal")) {
217 if (strict_strtoul(argv[2], 10, &value) < 0 ||
218 (value != 0 && value != 1)) {
219 printf("invalid value %s\n", argv[2]);
220 } else {
221 config->p_cal_present = value;
222 printf("cal = %d\n", config->p_cal_present);
223 }
224 return;
225 }
Patrick Delaunay01a75102019-04-10 14:09:27 +0200226 printf("argument %s invalid\n", argv[1]);
227}
228
229static bool stm32mp1_do_freq(struct ddr_info *priv,
Simon Glass09140112020-05-10 11:40:03 -0600230 int argc, char *const argv[])
Patrick Delaunay01a75102019-04-10 14:09:27 +0200231{
232 unsigned long ddrphy_clk;
233
234 if (argc == 2) {
235 if (strict_strtoul(argv[1], 0, &ddrphy_clk) < 0) {
236 printf("invalid argument %s", argv[1]);
237 return false;
238 }
239 if (clk_set_rate(&priv->clk, ddrphy_clk * 1000)) {
240 printf("ERROR: update failed!\n");
241 return false;
242 }
243 }
244 ddrphy_clk = clk_get_rate(&priv->clk);
245 printf("DDRPHY = %ld kHz\n", ddrphy_clk / 1000);
246 if (argc == 2)
247 return true;
248 return false;
249}
250
251static void stm32mp1_do_param(enum stm32mp1_ddr_interact_step step,
252 const struct stm32mp1_ddr_config *config,
Simon Glass09140112020-05-10 11:40:03 -0600253 int argc, char *const argv[])
Patrick Delaunay01a75102019-04-10 14:09:27 +0200254{
255 switch (argc) {
256 case 1:
257 stm32mp1_dump_param(config, NULL);
258 break;
259 case 2:
260 if (stm32mp1_dump_param(config, argv[1]))
261 printf("invalid argument %s\n",
262 argv[1]);
263 break;
264 case 3:
265 if (!stm32mp1_check_step(step, STEP_DDR_RESET))
266 return;
267 stm32mp1_edit_param(config, argv[1], argv[2]);
268 break;
269 }
270}
271
272static void stm32mp1_do_print(struct ddr_info *priv,
Simon Glass09140112020-05-10 11:40:03 -0600273 int argc, char *const argv[])
Patrick Delaunay01a75102019-04-10 14:09:27 +0200274{
275 switch (argc) {
276 case 1:
277 stm32mp1_dump_reg(priv, NULL);
278 break;
279 case 2:
280 if (stm32mp1_dump_reg(priv, argv[1]))
281 printf("invalid argument %s\n",
282 argv[1]);
283 break;
284 }
285}
286
287static int stm32mp1_do_step(enum stm32mp1_ddr_interact_step step,
Simon Glass09140112020-05-10 11:40:03 -0600288 int argc, char *const argv[])
Patrick Delaunay01a75102019-04-10 14:09:27 +0200289{
290 int i;
291 unsigned long value;
292
293 switch (argc) {
294 case 1:
295 for (i = 0; i < ARRAY_SIZE(step_str); i++)
296 printf("%d:%s\n", i, step_str[i]);
297 break;
298
299 case 2:
300 if ((strict_strtoul(argv[1], 0,
301 &value) < 0) ||
302 value >= ARRAY_SIZE(step_str)) {
303 printf("invalid argument %s\n",
304 argv[1]);
305 goto end;
306 }
307
308 if (value != STEP_DDR_RESET &&
309 value <= step) {
310 printf("invalid target %d:%s, current step is %d:%s\n",
311 (int)value, step_str[value],
312 step, step_str[step]);
313 goto end;
314 }
315 printf("step to %d:%s\n",
316 (int)value, step_str[value]);
317 return (int)value;
318 };
319
320end:
321 return step;
322}
323
Patrick Delaunay187c41d2019-04-10 14:09:29 +0200324#if defined(CONFIG_STM32MP1_DDR_TESTS) || defined(CONFIG_STM32MP1_DDR_TUNING)
Patrick Delaunay0d447522019-04-10 14:09:28 +0200325static const char * const s_result[] = {
326 [TEST_PASSED] = "Pass",
327 [TEST_FAILED] = "Failed",
328 [TEST_ERROR] = "Error"
329};
330
331static void stm32mp1_ddr_subcmd(struct ddr_info *priv,
332 int argc, char *argv[],
333 const struct test_desc array[],
334 const int array_nb)
335{
336 int i;
337 unsigned long value;
338 int result;
339 char string[50] = "";
340
341 if (argc == 1) {
342 printf("%s:%d\n", argv[0], array_nb);
343 for (i = 0; i < array_nb; i++)
344 printf("%d:%s:%s\n",
345 i, array[i].name, array[i].usage);
346 return;
347 }
348 if (argc > 1 && !strcmp(argv[1], "help")) {
349 printf("%s:%d\n", argv[0], array_nb);
350 for (i = 0; i < array_nb; i++)
351 printf("%d:%s:%s:%s\n", i,
352 array[i].name, array[i].usage, array[i].help);
353 return;
354 }
355
356 if ((strict_strtoul(argv[1], 0, &value) < 0) ||
357 value >= array_nb) {
358 sprintf(string, "invalid argument %s",
359 argv[1]);
360 result = TEST_FAILED;
361 goto end;
362 }
363
364 if (argc > (array[value].max_args + 2)) {
365 sprintf(string, "invalid nb of args %d, max %d",
366 argc - 2, array[value].max_args);
367 result = TEST_FAILED;
368 goto end;
369 }
370
371 printf("execute %d:%s\n", (int)value, array[value].name);
372 clear_ctrlc();
373 result = array[value].fct(priv->ctl, priv->phy,
374 string, argc - 2, &argv[2]);
375
376end:
377 printf("Result: %s [%s]\n", s_result[result], string);
378}
379#endif
380
Patrick Delaunay01a75102019-04-10 14:09:27 +0200381bool stm32mp1_ddr_interactive(void *priv,
382 enum stm32mp1_ddr_interact_step step,
383 const struct stm32mp1_ddr_config *config)
384{
Patrick Delaunay01a75102019-04-10 14:09:27 +0200385 char buffer[CONFIG_SYS_CBSIZE];
386 char *argv[CONFIG_SYS_MAXARGS + 1]; /* NULL terminated */
387 int argc;
388 static int next_step = -1;
389
390 if (next_step < 0 && step == STEP_DDR_RESET) {
391#ifdef CONFIG_STM32MP1_DDR_INTERACTIVE_FORCE
392 gd->flags &= ~(GD_FLG_SILENT |
393 GD_FLG_DISABLE_CONSOLE);
394 next_step = STEP_DDR_RESET;
395#else
396 unsigned long start = get_timer(0);
397
398 while (1) {
Heinrich Schuchardtc670aee2020-10-07 18:11:48 +0200399 if (tstc() && (getchar() == 'd')) {
Patrick Delaunay01a75102019-04-10 14:09:27 +0200400 next_step = STEP_DDR_RESET;
401 break;
402 }
403 if (get_timer(start) > 100)
404 break;
405 }
406#endif
407 }
408
Patrick Delaunay66b3b9d2020-11-06 19:01:36 +0100409 log_debug("** step %d ** %s / %d\n", step, step_str[step], next_step);
Patrick Delaunay01a75102019-04-10 14:09:27 +0200410
411 if (next_step < 0)
412 return false;
413
414 if (step < 0 || step > ARRAY_SIZE(step_str)) {
415 printf("** step %d ** INVALID\n", step);
416 return false;
417 }
418
419 printf("%d:%s\n", step, step_str[step]);
Patrick Delaunay01a75102019-04-10 14:09:27 +0200420
421 if (next_step > step)
422 return false;
423
424 while (next_step == step) {
Patrick Delaunay1c55a912020-03-06 11:14:05 +0100425 cli_readline_into_buffer("DDR>", buffer, 0);
Patrick Delaunay01a75102019-04-10 14:09:27 +0200426 argc = cli_simple_parse_line(buffer, argv);
427 if (!argc)
428 continue;
429
430 switch (stm32mp1_get_command(argv[0], argc)) {
431 case DDR_CMD_HELP:
432 stm32mp1_do_usage();
433 break;
434
435 case DDR_CMD_INFO:
436 stm32mp1_do_info(priv,
437 (struct stm32mp1_ddr_config *)config,
438 step, argc, argv);
439 break;
440
441 case DDR_CMD_FREQ:
442 if (stm32mp1_do_freq(priv, argc, argv))
443 next_step = STEP_DDR_RESET;
444 break;
445
446 case DDR_CMD_RESET:
447 do_reset(NULL, 0, 0, NULL);
448 break;
449
450 case DDR_CMD_PARAM:
451 stm32mp1_do_param(step, config, argc, argv);
452 break;
453
454 case DDR_CMD_PRINT:
455 stm32mp1_do_print(priv, argc, argv);
456 break;
457
458 case DDR_CMD_EDIT:
459 stm32mp1_edit_reg(priv, argv[1], argv[2]);
460 break;
461
462 case DDR_CMD_GO:
463 next_step = STEP_RUN;
464 break;
465
466 case DDR_CMD_NEXT:
467 next_step = step + 1;
468 break;
469
470 case DDR_CMD_STEP:
471 next_step = stm32mp1_do_step(step, argc, argv);
472 break;
473
Patrick Delaunay0d447522019-04-10 14:09:28 +0200474#ifdef CONFIG_STM32MP1_DDR_TESTS
475 case DDR_CMD_TEST:
476 if (!stm32mp1_check_step(step, STEP_DDR_READY))
477 continue;
478 stm32mp1_ddr_subcmd(priv, argc, argv, test, test_nb);
479 break;
480#endif
481
Patrick Delaunay187c41d2019-04-10 14:09:29 +0200482#ifdef CONFIG_STM32MP1_DDR_TUNING
483 case DDR_CMD_TUNING:
484 if (!stm32mp1_check_step(step, STEP_DDR_READY))
485 continue;
486 stm32mp1_ddr_subcmd(priv, argc, argv,
487 tuning, tuning_nb);
488 break;
489#endif
490
Patrick Delaunay01a75102019-04-10 14:09:27 +0200491 default:
492 break;
493 }
494 }
495 return next_step == STEP_DDR_RESET;
496}