blob: 2c19847c663343f8ce5c25d7fdb2cab6edc89f5c [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,
Patrick Delaunay01a75102019-04-10 14:09:27 +020035 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 Delaunay01a75102019-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 Delaunay0d447522019-04-10 14:09:28 +020075#ifdef CONFIG_STM32MP1_DDR_TESTS
76 [DDR_CMD_TEST] = { 0, 255 },
77#endif
Patrick Delaunay01a75102019-04-10 14:09:27 +020078 };
79 int i;
80
81 for (i = 0; i < DDR_CMD_UNKNOWN; i++)
82 if (!strcmp(cmd, cmd_string[i])) {
83 if (argc - 1 < cmd_arg[i][0]) {
84 printf("no enought argument (min=%d)\n",
85 cmd_arg[i][0]);
86 return DDR_CMD_UNKNOWN;
87 } else if (argc - 1 > cmd_arg[i][1]) {
88 printf("too many argument (max=%d)\n",
89 cmd_arg[i][1]);
90 return DDR_CMD_UNKNOWN;
91 } else {
92 return i;
93 }
94 }
95
96 printf("unknown command %s\n", cmd);
97 return DDR_CMD_UNKNOWN;
98}
99
100static void stm32mp1_do_usage(void)
101{
102 const char *usage = {
103 "commands:\n\n"
104 "help displays help\n"
105 "info displays DDR information\n"
106 "info <param> <val> changes DDR information\n"
Patrick Delaunay9819fe32021-11-15 15:32:29 +0100107 " with <param> = step, name, size or speed\n"
Patrick Delaunay01a75102019-04-10 14:09:27 +0200108 "freq displays the DDR PHY frequency in kHz\n"
109 "freq <freq> changes the DDR PHY frequency\n"
110 "param [type|reg] prints input parameters\n"
111 "param <reg> <val> edits parameters in step 0\n"
112 "print [type|reg] dumps registers\n"
113 "edit <reg> <val> modifies one register\n"
114 "step lists the available step\n"
115 "step <n> go to the step <n>\n"
116 "next goes to the next step\n"
117 "go continues the U-Boot SPL execution\n"
118 "reset reboots machine\n"
Patrick Delaunay0d447522019-04-10 14:09:28 +0200119#ifdef CONFIG_STM32MP1_DDR_TESTS
120 "test [help] | <n> [...] lists (with help) or executes test <n>\n"
121#endif
Patrick Delaunay01a75102019-04-10 14:09:27 +0200122 "\nwith for [type|reg]:\n"
123 " all registers if absent\n"
124 " <type> = ctl, phy\n"
Patrick Delaunay9819fe32021-11-15 15:32:29 +0100125 " or one category (static, timing, map, perf, dyn)\n"
Patrick Delaunay01a75102019-04-10 14:09:27 +0200126 " <reg> = name of the register\n"
127 };
128
129 puts(usage);
130}
131
132static bool stm32mp1_check_step(enum stm32mp1_ddr_interact_step step,
133 enum stm32mp1_ddr_interact_step expected)
134{
135 if (step != expected) {
136 printf("invalid step %d:%s expecting %d:%s\n",
137 step, step_str[step],
138 expected,
139 step_str[expected]);
140 return false;
141 }
142 return true;
143}
144
145static void stm32mp1_do_info(struct ddr_info *priv,
146 struct stm32mp1_ddr_config *config,
147 enum stm32mp1_ddr_interact_step step,
Simon Glass09140112020-05-10 11:40:03 -0600148 int argc, char *const argv[])
Patrick Delaunay01a75102019-04-10 14:09:27 +0200149{
150 unsigned long value;
151 static char *ddr_name;
152
153 if (argc == 1) {
154 printf("step = %d : %s\n", step, step_str[step]);
155 printf("name = %s\n", config->info.name);
156 printf("size = 0x%x\n", config->info.size);
157 printf("speed = %d kHz\n", config->info.speed);
158 return;
159 }
160
161 if (argc < 3) {
162 printf("no enought parameter\n");
163 return;
164 }
165 if (!strcmp(argv[1], "name")) {
166 u32 i, name_len = 0;
167
168 for (i = 2; i < argc; i++)
169 name_len += strlen(argv[i]) + 1;
170 if (ddr_name)
171 free(ddr_name);
172 ddr_name = malloc(name_len);
173 config->info.name = ddr_name;
174 if (!ddr_name) {
175 printf("alloc error, length %d\n", name_len);
176 return;
177 }
178 strcpy(ddr_name, argv[2]);
179 for (i = 3; i < argc; i++) {
180 strcat(ddr_name, " ");
181 strcat(ddr_name, argv[i]);
182 }
183 printf("name = %s\n", ddr_name);
184 return;
185 }
186 if (!strcmp(argv[1], "size")) {
187 if (strict_strtoul(argv[2], 16, &value) < 0) {
188 printf("invalid value %s\n", argv[2]);
189 } else {
190 config->info.size = value;
191 printf("size = 0x%x\n", config->info.size);
192 }
193 return;
194 }
195 if (!strcmp(argv[1], "speed")) {
196 if (strict_strtoul(argv[2], 10, &value) < 0) {
197 printf("invalid value %s\n", argv[2]);
198 } else {
199 config->info.speed = value;
200 printf("speed = %d kHz\n", config->info.speed);
201 value = clk_get_rate(&priv->clk);
202 printf("DDRPHY = %ld kHz\n", value / 1000);
203 }
204 return;
205 }
206 printf("argument %s invalid\n", argv[1]);
207}
208
209static bool stm32mp1_do_freq(struct ddr_info *priv,
Simon Glass09140112020-05-10 11:40:03 -0600210 int argc, char *const argv[])
Patrick Delaunay01a75102019-04-10 14:09:27 +0200211{
212 unsigned long ddrphy_clk;
213
214 if (argc == 2) {
215 if (strict_strtoul(argv[1], 0, &ddrphy_clk) < 0) {
216 printf("invalid argument %s", argv[1]);
217 return false;
218 }
219 if (clk_set_rate(&priv->clk, ddrphy_clk * 1000)) {
220 printf("ERROR: update failed!\n");
221 return false;
222 }
223 }
224 ddrphy_clk = clk_get_rate(&priv->clk);
225 printf("DDRPHY = %ld kHz\n", ddrphy_clk / 1000);
226 if (argc == 2)
227 return true;
228 return false;
229}
230
231static void stm32mp1_do_param(enum stm32mp1_ddr_interact_step step,
232 const struct stm32mp1_ddr_config *config,
Simon Glass09140112020-05-10 11:40:03 -0600233 int argc, char *const argv[])
Patrick Delaunay01a75102019-04-10 14:09:27 +0200234{
235 switch (argc) {
236 case 1:
237 stm32mp1_dump_param(config, NULL);
238 break;
239 case 2:
240 if (stm32mp1_dump_param(config, argv[1]))
241 printf("invalid argument %s\n",
242 argv[1]);
243 break;
244 case 3:
245 if (!stm32mp1_check_step(step, STEP_DDR_RESET))
246 return;
247 stm32mp1_edit_param(config, argv[1], argv[2]);
248 break;
249 }
250}
251
252static void stm32mp1_do_print(struct ddr_info *priv,
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_reg(priv, NULL);
258 break;
259 case 2:
260 if (stm32mp1_dump_reg(priv, argv[1]))
261 printf("invalid argument %s\n",
262 argv[1]);
263 break;
264 }
265}
266
267static int stm32mp1_do_step(enum stm32mp1_ddr_interact_step step,
Simon Glass09140112020-05-10 11:40:03 -0600268 int argc, char *const argv[])
Patrick Delaunay01a75102019-04-10 14:09:27 +0200269{
270 int i;
271 unsigned long value;
272
273 switch (argc) {
274 case 1:
275 for (i = 0; i < ARRAY_SIZE(step_str); i++)
276 printf("%d:%s\n", i, step_str[i]);
277 break;
278
279 case 2:
280 if ((strict_strtoul(argv[1], 0,
281 &value) < 0) ||
282 value >= ARRAY_SIZE(step_str)) {
283 printf("invalid argument %s\n",
284 argv[1]);
285 goto end;
286 }
287
288 if (value != STEP_DDR_RESET &&
289 value <= step) {
290 printf("invalid target %d:%s, current step is %d:%s\n",
291 (int)value, step_str[value],
292 step, step_str[step]);
293 goto end;
294 }
295 printf("step to %d:%s\n",
296 (int)value, step_str[value]);
297 return (int)value;
298 };
299
300end:
301 return step;
302}
303
Patrick Delaunayb3c29dc2021-11-15 15:32:30 +0100304#if defined(CONFIG_STM32MP1_DDR_TESTS)
Patrick Delaunay0d447522019-04-10 14:09:28 +0200305static const char * const s_result[] = {
306 [TEST_PASSED] = "Pass",
307 [TEST_FAILED] = "Failed",
308 [TEST_ERROR] = "Error"
309};
310
311static void stm32mp1_ddr_subcmd(struct ddr_info *priv,
312 int argc, char *argv[],
313 const struct test_desc array[],
314 const int array_nb)
315{
316 int i;
317 unsigned long value;
318 int result;
319 char string[50] = "";
320
321 if (argc == 1) {
322 printf("%s:%d\n", argv[0], array_nb);
323 for (i = 0; i < array_nb; i++)
324 printf("%d:%s:%s\n",
325 i, array[i].name, array[i].usage);
326 return;
327 }
328 if (argc > 1 && !strcmp(argv[1], "help")) {
329 printf("%s:%d\n", argv[0], array_nb);
330 for (i = 0; i < array_nb; i++)
331 printf("%d:%s:%s:%s\n", i,
332 array[i].name, array[i].usage, array[i].help);
333 return;
334 }
335
336 if ((strict_strtoul(argv[1], 0, &value) < 0) ||
337 value >= array_nb) {
338 sprintf(string, "invalid argument %s",
339 argv[1]);
340 result = TEST_FAILED;
341 goto end;
342 }
343
344 if (argc > (array[value].max_args + 2)) {
345 sprintf(string, "invalid nb of args %d, max %d",
346 argc - 2, array[value].max_args);
347 result = TEST_FAILED;
348 goto end;
349 }
350
351 printf("execute %d:%s\n", (int)value, array[value].name);
352 clear_ctrlc();
353 result = array[value].fct(priv->ctl, priv->phy,
354 string, argc - 2, &argv[2]);
355
356end:
357 printf("Result: %s [%s]\n", s_result[result], string);
358}
359#endif
360
Patrick Delaunay01a75102019-04-10 14:09:27 +0200361bool stm32mp1_ddr_interactive(void *priv,
362 enum stm32mp1_ddr_interact_step step,
363 const struct stm32mp1_ddr_config *config)
364{
Patrick Delaunay01a75102019-04-10 14:09:27 +0200365 char buffer[CONFIG_SYS_CBSIZE];
366 char *argv[CONFIG_SYS_MAXARGS + 1]; /* NULL terminated */
367 int argc;
368 static int next_step = -1;
369
370 if (next_step < 0 && step == STEP_DDR_RESET) {
371#ifdef CONFIG_STM32MP1_DDR_INTERACTIVE_FORCE
372 gd->flags &= ~(GD_FLG_SILENT |
373 GD_FLG_DISABLE_CONSOLE);
374 next_step = STEP_DDR_RESET;
375#else
376 unsigned long start = get_timer(0);
377
378 while (1) {
Heinrich Schuchardtc670aee2020-10-07 18:11:48 +0200379 if (tstc() && (getchar() == 'd')) {
Patrick Delaunay01a75102019-04-10 14:09:27 +0200380 next_step = STEP_DDR_RESET;
381 break;
382 }
383 if (get_timer(start) > 100)
384 break;
385 }
386#endif
387 }
388
Patrick Delaunay66b3b9d2020-11-06 19:01:36 +0100389 log_debug("** step %d ** %s / %d\n", step, step_str[step], next_step);
Patrick Delaunay01a75102019-04-10 14:09:27 +0200390
391 if (next_step < 0)
392 return false;
393
Rasmus Villemoesdaf07212023-03-24 08:55:19 +0100394 if (step < 0 || step >= ARRAY_SIZE(step_str)) {
Patrick Delaunay01a75102019-04-10 14:09:27 +0200395 printf("** step %d ** INVALID\n", step);
396 return false;
397 }
398
399 printf("%d:%s\n", step, step_str[step]);
Patrick Delaunay01a75102019-04-10 14:09:27 +0200400
401 if (next_step > step)
402 return false;
403
404 while (next_step == step) {
Patrick Delaunay1c55a912020-03-06 11:14:05 +0100405 cli_readline_into_buffer("DDR>", buffer, 0);
Patrick Delaunay01a75102019-04-10 14:09:27 +0200406 argc = cli_simple_parse_line(buffer, argv);
407 if (!argc)
408 continue;
409
410 switch (stm32mp1_get_command(argv[0], argc)) {
411 case DDR_CMD_HELP:
412 stm32mp1_do_usage();
413 break;
414
415 case DDR_CMD_INFO:
416 stm32mp1_do_info(priv,
417 (struct stm32mp1_ddr_config *)config,
418 step, argc, argv);
419 break;
420
421 case DDR_CMD_FREQ:
422 if (stm32mp1_do_freq(priv, argc, argv))
423 next_step = STEP_DDR_RESET;
424 break;
425
426 case DDR_CMD_RESET:
427 do_reset(NULL, 0, 0, NULL);
428 break;
429
430 case DDR_CMD_PARAM:
431 stm32mp1_do_param(step, config, argc, argv);
432 break;
433
434 case DDR_CMD_PRINT:
435 stm32mp1_do_print(priv, argc, argv);
436 break;
437
438 case DDR_CMD_EDIT:
439 stm32mp1_edit_reg(priv, argv[1], argv[2]);
440 break;
441
442 case DDR_CMD_GO:
443 next_step = STEP_RUN;
444 break;
445
446 case DDR_CMD_NEXT:
447 next_step = step + 1;
448 break;
449
450 case DDR_CMD_STEP:
451 next_step = stm32mp1_do_step(step, argc, argv);
452 break;
453
Patrick Delaunay0d447522019-04-10 14:09:28 +0200454#ifdef CONFIG_STM32MP1_DDR_TESTS
455 case DDR_CMD_TEST:
456 if (!stm32mp1_check_step(step, STEP_DDR_READY))
457 continue;
458 stm32mp1_ddr_subcmd(priv, argc, argv, test, test_nb);
459 break;
460#endif
Patrick Delaunay01a75102019-04-10 14:09:27 +0200461 default:
462 break;
463 }
464 }
465 return next_step == STEP_DDR_RESET;
466}