blob: 38390c0d552cd5120669278819a19cafd46a55bc [file] [log] [blame]
Patrick Delaunaybbee2702019-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
6#include <common.h>
Simon Glassed38aef2020-05-10 11:40:03 -06007#include <command.h>
Patrick Delaunaybbee2702019-04-10 14:09:27 +02008#include <console.h>
9#include <cli.h>
10#include <clk.h>
Simon Glass0f2af882020-05-10 11:40:05 -060011#include <log.h>
Patrick Delaunaybbee2702019-04-10 14:09:27 +020012#include <malloc.h>
13#include <ram.h>
14#include <reset.h>
15#include "stm32mp1_ddr.h"
Patrick Delaunay2b1c8182019-04-10 14:09:28 +020016#include "stm32mp1_tests.h"
Patrick Delaunaybbee2702019-04-10 14:09:27 +020017
18DECLARE_GLOBAL_DATA_PTR;
19
20enum ddr_command {
21 DDR_CMD_HELP,
22 DDR_CMD_INFO,
23 DDR_CMD_FREQ,
24 DDR_CMD_RESET,
25 DDR_CMD_PARAM,
26 DDR_CMD_PRINT,
27 DDR_CMD_EDIT,
28 DDR_CMD_STEP,
29 DDR_CMD_NEXT,
30 DDR_CMD_GO,
31 DDR_CMD_TEST,
32 DDR_CMD_TUNING,
33 DDR_CMD_UNKNOWN,
34};
35
36const char *step_str[] = {
37 [STEP_DDR_RESET] = "DDR_RESET",
38 [STEP_CTL_INIT] = "DDR_CTRL_INIT_DONE",
39 [STEP_PHY_INIT] = "DDR PHY_INIT_DONE",
40 [STEP_DDR_READY] = "DDR_READY",
41 [STEP_RUN] = "RUN"
42};
43
44enum ddr_command stm32mp1_get_command(char *cmd, int argc)
45{
46 const char *cmd_string[DDR_CMD_UNKNOWN] = {
47 [DDR_CMD_HELP] = "help",
48 [DDR_CMD_INFO] = "info",
49 [DDR_CMD_FREQ] = "freq",
50 [DDR_CMD_RESET] = "reset",
51 [DDR_CMD_PARAM] = "param",
52 [DDR_CMD_PRINT] = "print",
53 [DDR_CMD_EDIT] = "edit",
54 [DDR_CMD_STEP] = "step",
55 [DDR_CMD_NEXT] = "next",
56 [DDR_CMD_GO] = "go",
Patrick Delaunay2b1c8182019-04-10 14:09:28 +020057#ifdef CONFIG_STM32MP1_DDR_TESTS
58 [DDR_CMD_TEST] = "test",
59#endif
Patrick Delaunay2f91e612019-04-10 14:09:29 +020060#ifdef CONFIG_STM32MP1_DDR_TUNING
61 [DDR_CMD_TUNING] = "tuning",
62#endif
Patrick Delaunaybbee2702019-04-10 14:09:27 +020063 };
64 /* min and max number of argument */
65 const char cmd_arg[DDR_CMD_UNKNOWN][2] = {
66 [DDR_CMD_HELP] = { 0, 0 },
67 [DDR_CMD_INFO] = { 0, 255 },
68 [DDR_CMD_FREQ] = { 0, 1 },
69 [DDR_CMD_RESET] = { 0, 0 },
70 [DDR_CMD_PARAM] = { 0, 2 },
71 [DDR_CMD_PRINT] = { 0, 1 },
72 [DDR_CMD_EDIT] = { 2, 2 },
73 [DDR_CMD_STEP] = { 0, 1 },
74 [DDR_CMD_NEXT] = { 0, 0 },
75 [DDR_CMD_GO] = { 0, 0 },
Patrick Delaunay2b1c8182019-04-10 14:09:28 +020076#ifdef CONFIG_STM32MP1_DDR_TESTS
77 [DDR_CMD_TEST] = { 0, 255 },
78#endif
Patrick Delaunay2f91e612019-04-10 14:09:29 +020079#ifdef CONFIG_STM32MP1_DDR_TUNING
80 [DDR_CMD_TUNING] = { 0, 255 },
81#endif
Patrick Delaunaybbee2702019-04-10 14:09:27 +020082 };
83 int i;
84
85 for (i = 0; i < DDR_CMD_UNKNOWN; i++)
86 if (!strcmp(cmd, cmd_string[i])) {
87 if (argc - 1 < cmd_arg[i][0]) {
88 printf("no enought argument (min=%d)\n",
89 cmd_arg[i][0]);
90 return DDR_CMD_UNKNOWN;
91 } else if (argc - 1 > cmd_arg[i][1]) {
92 printf("too many argument (max=%d)\n",
93 cmd_arg[i][1]);
94 return DDR_CMD_UNKNOWN;
95 } else {
96 return i;
97 }
98 }
99
100 printf("unknown command %s\n", cmd);
101 return DDR_CMD_UNKNOWN;
102}
103
104static void stm32mp1_do_usage(void)
105{
106 const char *usage = {
107 "commands:\n\n"
108 "help displays help\n"
109 "info displays DDR information\n"
110 "info <param> <val> changes DDR information\n"
Patrick Delaunaya68e2d62020-03-06 11:14:11 +0100111 " with <param> = step, name, size, speed or cal\n"
Patrick Delaunaybbee2702019-04-10 14:09:27 +0200112 "freq displays the DDR PHY frequency in kHz\n"
113 "freq <freq> changes the DDR PHY frequency\n"
114 "param [type|reg] prints input parameters\n"
115 "param <reg> <val> edits parameters in step 0\n"
116 "print [type|reg] dumps registers\n"
117 "edit <reg> <val> modifies one register\n"
118 "step lists the available step\n"
119 "step <n> go to the step <n>\n"
120 "next goes to the next step\n"
121 "go continues the U-Boot SPL execution\n"
122 "reset reboots machine\n"
Patrick Delaunay2b1c8182019-04-10 14:09:28 +0200123#ifdef CONFIG_STM32MP1_DDR_TESTS
124 "test [help] | <n> [...] lists (with help) or executes test <n>\n"
125#endif
Patrick Delaunay2f91e612019-04-10 14:09:29 +0200126#ifdef CONFIG_STM32MP1_DDR_TUNING
127 "tuning [help] | <n> [...] lists (with help) or execute tuning <n>\n"
128#endif
Patrick Delaunaybbee2702019-04-10 14:09:27 +0200129 "\nwith for [type|reg]:\n"
130 " all registers if absent\n"
131 " <type> = ctl, phy\n"
132 " or one category (static, timing, map, perf, cal, dyn)\n"
133 " <reg> = name of the register\n"
134 };
135
136 puts(usage);
137}
138
139static bool stm32mp1_check_step(enum stm32mp1_ddr_interact_step step,
140 enum stm32mp1_ddr_interact_step expected)
141{
142 if (step != expected) {
143 printf("invalid step %d:%s expecting %d:%s\n",
144 step, step_str[step],
145 expected,
146 step_str[expected]);
147 return false;
148 }
149 return true;
150}
151
152static void stm32mp1_do_info(struct ddr_info *priv,
153 struct stm32mp1_ddr_config *config,
154 enum stm32mp1_ddr_interact_step step,
Simon Glassed38aef2020-05-10 11:40:03 -0600155 int argc, char *const argv[])
Patrick Delaunaybbee2702019-04-10 14:09:27 +0200156{
157 unsigned long value;
158 static char *ddr_name;
159
160 if (argc == 1) {
161 printf("step = %d : %s\n", step, step_str[step]);
162 printf("name = %s\n", config->info.name);
163 printf("size = 0x%x\n", config->info.size);
164 printf("speed = %d kHz\n", config->info.speed);
Patrick Delaunaya68e2d62020-03-06 11:14:11 +0100165 printf("cal = %d\n", config->p_cal_present);
Patrick Delaunaybbee2702019-04-10 14:09:27 +0200166 return;
167 }
168
169 if (argc < 3) {
170 printf("no enought parameter\n");
171 return;
172 }
173 if (!strcmp(argv[1], "name")) {
174 u32 i, name_len = 0;
175
176 for (i = 2; i < argc; i++)
177 name_len += strlen(argv[i]) + 1;
178 if (ddr_name)
179 free(ddr_name);
180 ddr_name = malloc(name_len);
181 config->info.name = ddr_name;
182 if (!ddr_name) {
183 printf("alloc error, length %d\n", name_len);
184 return;
185 }
186 strcpy(ddr_name, argv[2]);
187 for (i = 3; i < argc; i++) {
188 strcat(ddr_name, " ");
189 strcat(ddr_name, argv[i]);
190 }
191 printf("name = %s\n", ddr_name);
192 return;
193 }
194 if (!strcmp(argv[1], "size")) {
195 if (strict_strtoul(argv[2], 16, &value) < 0) {
196 printf("invalid value %s\n", argv[2]);
197 } else {
198 config->info.size = value;
199 printf("size = 0x%x\n", config->info.size);
200 }
201 return;
202 }
203 if (!strcmp(argv[1], "speed")) {
204 if (strict_strtoul(argv[2], 10, &value) < 0) {
205 printf("invalid value %s\n", argv[2]);
206 } else {
207 config->info.speed = value;
208 printf("speed = %d kHz\n", config->info.speed);
209 value = clk_get_rate(&priv->clk);
210 printf("DDRPHY = %ld kHz\n", value / 1000);
211 }
212 return;
213 }
Patrick Delaunaya68e2d62020-03-06 11:14:11 +0100214 if (!strcmp(argv[1], "cal")) {
215 if (strict_strtoul(argv[2], 10, &value) < 0 ||
216 (value != 0 && value != 1)) {
217 printf("invalid value %s\n", argv[2]);
218 } else {
219 config->p_cal_present = value;
220 printf("cal = %d\n", config->p_cal_present);
221 }
222 return;
223 }
Patrick Delaunaybbee2702019-04-10 14:09:27 +0200224 printf("argument %s invalid\n", argv[1]);
225}
226
227static bool stm32mp1_do_freq(struct ddr_info *priv,
Simon Glassed38aef2020-05-10 11:40:03 -0600228 int argc, char *const argv[])
Patrick Delaunaybbee2702019-04-10 14:09:27 +0200229{
230 unsigned long ddrphy_clk;
231
232 if (argc == 2) {
233 if (strict_strtoul(argv[1], 0, &ddrphy_clk) < 0) {
234 printf("invalid argument %s", argv[1]);
235 return false;
236 }
237 if (clk_set_rate(&priv->clk, ddrphy_clk * 1000)) {
238 printf("ERROR: update failed!\n");
239 return false;
240 }
241 }
242 ddrphy_clk = clk_get_rate(&priv->clk);
243 printf("DDRPHY = %ld kHz\n", ddrphy_clk / 1000);
244 if (argc == 2)
245 return true;
246 return false;
247}
248
249static void stm32mp1_do_param(enum stm32mp1_ddr_interact_step step,
250 const struct stm32mp1_ddr_config *config,
Simon Glassed38aef2020-05-10 11:40:03 -0600251 int argc, char *const argv[])
Patrick Delaunaybbee2702019-04-10 14:09:27 +0200252{
253 switch (argc) {
254 case 1:
255 stm32mp1_dump_param(config, NULL);
256 break;
257 case 2:
258 if (stm32mp1_dump_param(config, argv[1]))
259 printf("invalid argument %s\n",
260 argv[1]);
261 break;
262 case 3:
263 if (!stm32mp1_check_step(step, STEP_DDR_RESET))
264 return;
265 stm32mp1_edit_param(config, argv[1], argv[2]);
266 break;
267 }
268}
269
270static void stm32mp1_do_print(struct ddr_info *priv,
Simon Glassed38aef2020-05-10 11:40:03 -0600271 int argc, char *const argv[])
Patrick Delaunaybbee2702019-04-10 14:09:27 +0200272{
273 switch (argc) {
274 case 1:
275 stm32mp1_dump_reg(priv, NULL);
276 break;
277 case 2:
278 if (stm32mp1_dump_reg(priv, argv[1]))
279 printf("invalid argument %s\n",
280 argv[1]);
281 break;
282 }
283}
284
285static int stm32mp1_do_step(enum stm32mp1_ddr_interact_step step,
Simon Glassed38aef2020-05-10 11:40:03 -0600286 int argc, char *const argv[])
Patrick Delaunaybbee2702019-04-10 14:09:27 +0200287{
288 int i;
289 unsigned long value;
290
291 switch (argc) {
292 case 1:
293 for (i = 0; i < ARRAY_SIZE(step_str); i++)
294 printf("%d:%s\n", i, step_str[i]);
295 break;
296
297 case 2:
298 if ((strict_strtoul(argv[1], 0,
299 &value) < 0) ||
300 value >= ARRAY_SIZE(step_str)) {
301 printf("invalid argument %s\n",
302 argv[1]);
303 goto end;
304 }
305
306 if (value != STEP_DDR_RESET &&
307 value <= step) {
308 printf("invalid target %d:%s, current step is %d:%s\n",
309 (int)value, step_str[value],
310 step, step_str[step]);
311 goto end;
312 }
313 printf("step to %d:%s\n",
314 (int)value, step_str[value]);
315 return (int)value;
316 };
317
318end:
319 return step;
320}
321
Patrick Delaunay2f91e612019-04-10 14:09:29 +0200322#if defined(CONFIG_STM32MP1_DDR_TESTS) || defined(CONFIG_STM32MP1_DDR_TUNING)
Patrick Delaunay2b1c8182019-04-10 14:09:28 +0200323static const char * const s_result[] = {
324 [TEST_PASSED] = "Pass",
325 [TEST_FAILED] = "Failed",
326 [TEST_ERROR] = "Error"
327};
328
329static void stm32mp1_ddr_subcmd(struct ddr_info *priv,
330 int argc, char *argv[],
331 const struct test_desc array[],
332 const int array_nb)
333{
334 int i;
335 unsigned long value;
336 int result;
337 char string[50] = "";
338
339 if (argc == 1) {
340 printf("%s:%d\n", argv[0], array_nb);
341 for (i = 0; i < array_nb; i++)
342 printf("%d:%s:%s\n",
343 i, array[i].name, array[i].usage);
344 return;
345 }
346 if (argc > 1 && !strcmp(argv[1], "help")) {
347 printf("%s:%d\n", argv[0], array_nb);
348 for (i = 0; i < array_nb; i++)
349 printf("%d:%s:%s:%s\n", i,
350 array[i].name, array[i].usage, array[i].help);
351 return;
352 }
353
354 if ((strict_strtoul(argv[1], 0, &value) < 0) ||
355 value >= array_nb) {
356 sprintf(string, "invalid argument %s",
357 argv[1]);
358 result = TEST_FAILED;
359 goto end;
360 }
361
362 if (argc > (array[value].max_args + 2)) {
363 sprintf(string, "invalid nb of args %d, max %d",
364 argc - 2, array[value].max_args);
365 result = TEST_FAILED;
366 goto end;
367 }
368
369 printf("execute %d:%s\n", (int)value, array[value].name);
370 clear_ctrlc();
371 result = array[value].fct(priv->ctl, priv->phy,
372 string, argc - 2, &argv[2]);
373
374end:
375 printf("Result: %s [%s]\n", s_result[result], string);
376}
377#endif
378
Patrick Delaunaybbee2702019-04-10 14:09:27 +0200379bool stm32mp1_ddr_interactive(void *priv,
380 enum stm32mp1_ddr_interact_step step,
381 const struct stm32mp1_ddr_config *config)
382{
Patrick Delaunaybbee2702019-04-10 14:09:27 +0200383 char buffer[CONFIG_SYS_CBSIZE];
384 char *argv[CONFIG_SYS_MAXARGS + 1]; /* NULL terminated */
385 int argc;
386 static int next_step = -1;
387
388 if (next_step < 0 && step == STEP_DDR_RESET) {
389#ifdef CONFIG_STM32MP1_DDR_INTERACTIVE_FORCE
390 gd->flags &= ~(GD_FLG_SILENT |
391 GD_FLG_DISABLE_CONSOLE);
392 next_step = STEP_DDR_RESET;
393#else
394 unsigned long start = get_timer(0);
395
396 while (1) {
397 if (tstc() && (getc() == 'd')) {
398 next_step = STEP_DDR_RESET;
399 break;
400 }
401 if (get_timer(start) > 100)
402 break;
403 }
404#endif
405 }
406
407 debug("** step %d ** %s / %d\n", step, step_str[step], next_step);
408
409 if (next_step < 0)
410 return false;
411
412 if (step < 0 || step > ARRAY_SIZE(step_str)) {
413 printf("** step %d ** INVALID\n", step);
414 return false;
415 }
416
417 printf("%d:%s\n", step, step_str[step]);
Patrick Delaunaybbee2702019-04-10 14:09:27 +0200418
419 if (next_step > step)
420 return false;
421
422 while (next_step == step) {
Patrick Delaunayeed33352020-03-06 11:14:05 +0100423 cli_readline_into_buffer("DDR>", buffer, 0);
Patrick Delaunaybbee2702019-04-10 14:09:27 +0200424 argc = cli_simple_parse_line(buffer, argv);
425 if (!argc)
426 continue;
427
428 switch (stm32mp1_get_command(argv[0], argc)) {
429 case DDR_CMD_HELP:
430 stm32mp1_do_usage();
431 break;
432
433 case DDR_CMD_INFO:
434 stm32mp1_do_info(priv,
435 (struct stm32mp1_ddr_config *)config,
436 step, argc, argv);
437 break;
438
439 case DDR_CMD_FREQ:
440 if (stm32mp1_do_freq(priv, argc, argv))
441 next_step = STEP_DDR_RESET;
442 break;
443
444 case DDR_CMD_RESET:
445 do_reset(NULL, 0, 0, NULL);
446 break;
447
448 case DDR_CMD_PARAM:
449 stm32mp1_do_param(step, config, argc, argv);
450 break;
451
452 case DDR_CMD_PRINT:
453 stm32mp1_do_print(priv, argc, argv);
454 break;
455
456 case DDR_CMD_EDIT:
457 stm32mp1_edit_reg(priv, argv[1], argv[2]);
458 break;
459
460 case DDR_CMD_GO:
461 next_step = STEP_RUN;
462 break;
463
464 case DDR_CMD_NEXT:
465 next_step = step + 1;
466 break;
467
468 case DDR_CMD_STEP:
469 next_step = stm32mp1_do_step(step, argc, argv);
470 break;
471
Patrick Delaunay2b1c8182019-04-10 14:09:28 +0200472#ifdef CONFIG_STM32MP1_DDR_TESTS
473 case DDR_CMD_TEST:
474 if (!stm32mp1_check_step(step, STEP_DDR_READY))
475 continue;
476 stm32mp1_ddr_subcmd(priv, argc, argv, test, test_nb);
477 break;
478#endif
479
Patrick Delaunay2f91e612019-04-10 14:09:29 +0200480#ifdef CONFIG_STM32MP1_DDR_TUNING
481 case DDR_CMD_TUNING:
482 if (!stm32mp1_check_step(step, STEP_DDR_READY))
483 continue;
484 stm32mp1_ddr_subcmd(priv, argc, argv,
485 tuning, tuning_nb);
486 break;
487#endif
488
Patrick Delaunaybbee2702019-04-10 14:09:27 +0200489 default:
490 break;
491 }
492 }
493 return next_step == STEP_DDR_RESET;
494}