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