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