blob: 6340afbb87093313ef0e05ebad1cd36cd0d55d3d [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
Patrick Delaunay753f5362020-11-06 19:01:36 +01006#define LOG_CATEGORY UCLASS_RAM
7
Simon Glassed38aef2020-05-10 11:40:03 -06008#include <command.h>
Patrick Delaunaybbee2702019-04-10 14:09:27 +02009#include <console.h>
10#include <cli.h>
11#include <clk.h>
Simon Glass0f2af882020-05-10 11:40:05 -060012#include <log.h>
Patrick Delaunaybbee2702019-04-10 14:09:27 +020013#include <malloc.h>
14#include <ram.h>
15#include <reset.h>
Simon Glass3ba929a2020-10-30 21:38:53 -060016#include <asm/global_data.h>
Patrick Delaunaybbee2702019-04-10 14:09:27 +020017#include "stm32mp1_ddr.h"
Patrick Delaunay2b1c8182019-04-10 14:09:28 +020018#include "stm32mp1_tests.h"
Patrick Delaunaybbee2702019-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,
Patrick Delaunaybbee2702019-04-10 14:09:27 +020034 DDR_CMD_UNKNOWN,
35};
36
37const char *step_str[] = {
38 [STEP_DDR_RESET] = "DDR_RESET",
39 [STEP_CTL_INIT] = "DDR_CTRL_INIT_DONE",
40 [STEP_PHY_INIT] = "DDR PHY_INIT_DONE",
41 [STEP_DDR_READY] = "DDR_READY",
42 [STEP_RUN] = "RUN"
43};
44
45enum ddr_command stm32mp1_get_command(char *cmd, int argc)
46{
47 const char *cmd_string[DDR_CMD_UNKNOWN] = {
48 [DDR_CMD_HELP] = "help",
49 [DDR_CMD_INFO] = "info",
50 [DDR_CMD_FREQ] = "freq",
51 [DDR_CMD_RESET] = "reset",
52 [DDR_CMD_PARAM] = "param",
53 [DDR_CMD_PRINT] = "print",
54 [DDR_CMD_EDIT] = "edit",
55 [DDR_CMD_STEP] = "step",
56 [DDR_CMD_NEXT] = "next",
57 [DDR_CMD_GO] = "go",
Patrick Delaunay2b1c8182019-04-10 14:09:28 +020058#ifdef CONFIG_STM32MP1_DDR_TESTS
59 [DDR_CMD_TEST] = "test",
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 Delaunaybbee2702019-04-10 14:09:27 +020077 };
78 int i;
79
80 for (i = 0; i < DDR_CMD_UNKNOWN; i++)
81 if (!strcmp(cmd, cmd_string[i])) {
82 if (argc - 1 < cmd_arg[i][0]) {
83 printf("no enought argument (min=%d)\n",
84 cmd_arg[i][0]);
85 return DDR_CMD_UNKNOWN;
86 } else if (argc - 1 > cmd_arg[i][1]) {
87 printf("too many argument (max=%d)\n",
88 cmd_arg[i][1]);
89 return DDR_CMD_UNKNOWN;
90 } else {
91 return i;
92 }
93 }
94
95 printf("unknown command %s\n", cmd);
96 return DDR_CMD_UNKNOWN;
97}
98
99static void stm32mp1_do_usage(void)
100{
101 const char *usage = {
102 "commands:\n\n"
103 "help displays help\n"
104 "info displays DDR information\n"
105 "info <param> <val> changes DDR information\n"
Patrick Delaunay9e2dd662021-11-15 15:32:29 +0100106 " with <param> = step, name, size or speed\n"
Patrick Delaunaybbee2702019-04-10 14:09:27 +0200107 "freq displays the DDR PHY frequency in kHz\n"
108 "freq <freq> changes the DDR PHY frequency\n"
109 "param [type|reg] prints input parameters\n"
110 "param <reg> <val> edits parameters in step 0\n"
111 "print [type|reg] dumps registers\n"
112 "edit <reg> <val> modifies one register\n"
113 "step lists the available step\n"
114 "step <n> go to the step <n>\n"
115 "next goes to the next step\n"
116 "go continues the U-Boot SPL execution\n"
117 "reset reboots machine\n"
Patrick Delaunay2b1c8182019-04-10 14:09:28 +0200118#ifdef CONFIG_STM32MP1_DDR_TESTS
119 "test [help] | <n> [...] lists (with help) or executes test <n>\n"
120#endif
Patrick Delaunaybbee2702019-04-10 14:09:27 +0200121 "\nwith for [type|reg]:\n"
122 " all registers if absent\n"
123 " <type> = ctl, phy\n"
Patrick Delaunay9e2dd662021-11-15 15:32:29 +0100124 " or one category (static, timing, map, perf, dyn)\n"
Patrick Delaunaybbee2702019-04-10 14:09:27 +0200125 " <reg> = name of the register\n"
126 };
127
128 puts(usage);
129}
130
131static bool stm32mp1_check_step(enum stm32mp1_ddr_interact_step step,
132 enum stm32mp1_ddr_interact_step expected)
133{
134 if (step != expected) {
135 printf("invalid step %d:%s expecting %d:%s\n",
136 step, step_str[step],
137 expected,
138 step_str[expected]);
139 return false;
140 }
141 return true;
142}
143
144static void stm32mp1_do_info(struct ddr_info *priv,
145 struct stm32mp1_ddr_config *config,
146 enum stm32mp1_ddr_interact_step step,
Simon Glassed38aef2020-05-10 11:40:03 -0600147 int argc, char *const argv[])
Patrick Delaunaybbee2702019-04-10 14:09:27 +0200148{
149 unsigned long value;
150 static char *ddr_name;
151
152 if (argc == 1) {
153 printf("step = %d : %s\n", step, step_str[step]);
154 printf("name = %s\n", config->info.name);
155 printf("size = 0x%x\n", config->info.size);
156 printf("speed = %d kHz\n", config->info.speed);
157 return;
158 }
159
160 if (argc < 3) {
161 printf("no enought parameter\n");
162 return;
163 }
164 if (!strcmp(argv[1], "name")) {
165 u32 i, name_len = 0;
166
167 for (i = 2; i < argc; i++)
168 name_len += strlen(argv[i]) + 1;
169 if (ddr_name)
170 free(ddr_name);
171 ddr_name = malloc(name_len);
172 config->info.name = ddr_name;
173 if (!ddr_name) {
174 printf("alloc error, length %d\n", name_len);
175 return;
176 }
177 strcpy(ddr_name, argv[2]);
178 for (i = 3; i < argc; i++) {
179 strcat(ddr_name, " ");
180 strcat(ddr_name, argv[i]);
181 }
182 printf("name = %s\n", ddr_name);
183 return;
184 }
185 if (!strcmp(argv[1], "size")) {
186 if (strict_strtoul(argv[2], 16, &value) < 0) {
187 printf("invalid value %s\n", argv[2]);
188 } else {
189 config->info.size = value;
190 printf("size = 0x%x\n", config->info.size);
191 }
192 return;
193 }
194 if (!strcmp(argv[1], "speed")) {
195 if (strict_strtoul(argv[2], 10, &value) < 0) {
196 printf("invalid value %s\n", argv[2]);
197 } else {
198 config->info.speed = value;
199 printf("speed = %d kHz\n", config->info.speed);
200 value = clk_get_rate(&priv->clk);
201 printf("DDRPHY = %ld kHz\n", value / 1000);
202 }
203 return;
204 }
205 printf("argument %s invalid\n", argv[1]);
206}
207
208static bool stm32mp1_do_freq(struct ddr_info *priv,
Simon Glassed38aef2020-05-10 11:40:03 -0600209 int argc, char *const argv[])
Patrick Delaunaybbee2702019-04-10 14:09:27 +0200210{
211 unsigned long ddrphy_clk;
212
213 if (argc == 2) {
214 if (strict_strtoul(argv[1], 0, &ddrphy_clk) < 0) {
215 printf("invalid argument %s", argv[1]);
216 return false;
217 }
218 if (clk_set_rate(&priv->clk, ddrphy_clk * 1000)) {
219 printf("ERROR: update failed!\n");
220 return false;
221 }
222 }
223 ddrphy_clk = clk_get_rate(&priv->clk);
224 printf("DDRPHY = %ld kHz\n", ddrphy_clk / 1000);
225 if (argc == 2)
226 return true;
227 return false;
228}
229
230static void stm32mp1_do_param(enum stm32mp1_ddr_interact_step step,
231 const struct stm32mp1_ddr_config *config,
Simon Glassed38aef2020-05-10 11:40:03 -0600232 int argc, char *const argv[])
Patrick Delaunaybbee2702019-04-10 14:09:27 +0200233{
234 switch (argc) {
235 case 1:
236 stm32mp1_dump_param(config, NULL);
237 break;
238 case 2:
239 if (stm32mp1_dump_param(config, argv[1]))
240 printf("invalid argument %s\n",
241 argv[1]);
242 break;
243 case 3:
244 if (!stm32mp1_check_step(step, STEP_DDR_RESET))
245 return;
246 stm32mp1_edit_param(config, argv[1], argv[2]);
247 break;
248 }
249}
250
251static void stm32mp1_do_print(struct ddr_info *priv,
Simon Glassed38aef2020-05-10 11:40:03 -0600252 int argc, char *const argv[])
Patrick Delaunaybbee2702019-04-10 14:09:27 +0200253{
254 switch (argc) {
255 case 1:
256 stm32mp1_dump_reg(priv, NULL);
257 break;
258 case 2:
259 if (stm32mp1_dump_reg(priv, argv[1]))
260 printf("invalid argument %s\n",
261 argv[1]);
262 break;
263 }
264}
265
266static int stm32mp1_do_step(enum stm32mp1_ddr_interact_step step,
Simon Glassed38aef2020-05-10 11:40:03 -0600267 int argc, char *const argv[])
Patrick Delaunaybbee2702019-04-10 14:09:27 +0200268{
269 int i;
270 unsigned long value;
271
272 switch (argc) {
273 case 1:
274 for (i = 0; i < ARRAY_SIZE(step_str); i++)
275 printf("%d:%s\n", i, step_str[i]);
276 break;
277
278 case 2:
279 if ((strict_strtoul(argv[1], 0,
280 &value) < 0) ||
281 value >= ARRAY_SIZE(step_str)) {
282 printf("invalid argument %s\n",
283 argv[1]);
284 goto end;
285 }
286
287 if (value != STEP_DDR_RESET &&
288 value <= step) {
289 printf("invalid target %d:%s, current step is %d:%s\n",
290 (int)value, step_str[value],
291 step, step_str[step]);
292 goto end;
293 }
294 printf("step to %d:%s\n",
295 (int)value, step_str[value]);
296 return (int)value;
297 };
298
299end:
300 return step;
301}
302
Patrick Delaunay0c4354e2021-11-15 15:32:30 +0100303#if defined(CONFIG_STM32MP1_DDR_TESTS)
Patrick Delaunay2b1c8182019-04-10 14:09:28 +0200304static const char * const s_result[] = {
305 [TEST_PASSED] = "Pass",
306 [TEST_FAILED] = "Failed",
307 [TEST_ERROR] = "Error"
308};
309
310static void stm32mp1_ddr_subcmd(struct ddr_info *priv,
311 int argc, char *argv[],
312 const struct test_desc array[],
313 const int array_nb)
314{
315 int i;
316 unsigned long value;
317 int result;
318 char string[50] = "";
319
320 if (argc == 1) {
321 printf("%s:%d\n", argv[0], array_nb);
322 for (i = 0; i < array_nb; i++)
323 printf("%d:%s:%s\n",
324 i, array[i].name, array[i].usage);
325 return;
326 }
327 if (argc > 1 && !strcmp(argv[1], "help")) {
328 printf("%s:%d\n", argv[0], array_nb);
329 for (i = 0; i < array_nb; i++)
330 printf("%d:%s:%s:%s\n", i,
331 array[i].name, array[i].usage, array[i].help);
332 return;
333 }
334
335 if ((strict_strtoul(argv[1], 0, &value) < 0) ||
336 value >= array_nb) {
337 sprintf(string, "invalid argument %s",
338 argv[1]);
339 result = TEST_FAILED;
340 goto end;
341 }
342
343 if (argc > (array[value].max_args + 2)) {
344 sprintf(string, "invalid nb of args %d, max %d",
345 argc - 2, array[value].max_args);
346 result = TEST_FAILED;
347 goto end;
348 }
349
350 printf("execute %d:%s\n", (int)value, array[value].name);
351 clear_ctrlc();
352 result = array[value].fct(priv->ctl, priv->phy,
353 string, argc - 2, &argv[2]);
354
355end:
356 printf("Result: %s [%s]\n", s_result[result], string);
357}
358#endif
359
Patrick Delaunaybbee2702019-04-10 14:09:27 +0200360bool stm32mp1_ddr_interactive(void *priv,
361 enum stm32mp1_ddr_interact_step step,
362 const struct stm32mp1_ddr_config *config)
363{
Patrick Delaunaybbee2702019-04-10 14:09:27 +0200364 char buffer[CONFIG_SYS_CBSIZE];
365 char *argv[CONFIG_SYS_MAXARGS + 1]; /* NULL terminated */
366 int argc;
367 static int next_step = -1;
368
369 if (next_step < 0 && step == STEP_DDR_RESET) {
370#ifdef CONFIG_STM32MP1_DDR_INTERACTIVE_FORCE
371 gd->flags &= ~(GD_FLG_SILENT |
372 GD_FLG_DISABLE_CONSOLE);
373 next_step = STEP_DDR_RESET;
374#else
375 unsigned long start = get_timer(0);
376
377 while (1) {
Heinrich Schuchardtc4954fb2020-10-07 18:11:48 +0200378 if (tstc() && (getchar() == 'd')) {
Patrick Delaunaybbee2702019-04-10 14:09:27 +0200379 next_step = STEP_DDR_RESET;
380 break;
381 }
382 if (get_timer(start) > 100)
383 break;
384 }
385#endif
386 }
387
Patrick Delaunay753f5362020-11-06 19:01:36 +0100388 log_debug("** step %d ** %s / %d\n", step, step_str[step], next_step);
Patrick Delaunaybbee2702019-04-10 14:09:27 +0200389
390 if (next_step < 0)
391 return false;
392
Rasmus Villemoes6d83f3c2023-03-24 08:55:19 +0100393 if (step < 0 || step >= ARRAY_SIZE(step_str)) {
Patrick Delaunaybbee2702019-04-10 14:09:27 +0200394 printf("** step %d ** INVALID\n", step);
395 return false;
396 }
397
398 printf("%d:%s\n", step, step_str[step]);
Patrick Delaunaybbee2702019-04-10 14:09:27 +0200399
400 if (next_step > step)
401 return false;
402
403 while (next_step == step) {
Patrick Delaunayeed33352020-03-06 11:14:05 +0100404 cli_readline_into_buffer("DDR>", buffer, 0);
Patrick Delaunaybbee2702019-04-10 14:09:27 +0200405 argc = cli_simple_parse_line(buffer, argv);
406 if (!argc)
407 continue;
408
409 switch (stm32mp1_get_command(argv[0], argc)) {
410 case DDR_CMD_HELP:
411 stm32mp1_do_usage();
412 break;
413
414 case DDR_CMD_INFO:
415 stm32mp1_do_info(priv,
416 (struct stm32mp1_ddr_config *)config,
417 step, argc, argv);
418 break;
419
420 case DDR_CMD_FREQ:
421 if (stm32mp1_do_freq(priv, argc, argv))
422 next_step = STEP_DDR_RESET;
423 break;
424
425 case DDR_CMD_RESET:
426 do_reset(NULL, 0, 0, NULL);
427 break;
428
429 case DDR_CMD_PARAM:
430 stm32mp1_do_param(step, config, argc, argv);
431 break;
432
433 case DDR_CMD_PRINT:
434 stm32mp1_do_print(priv, argc, argv);
435 break;
436
437 case DDR_CMD_EDIT:
438 stm32mp1_edit_reg(priv, argv[1], argv[2]);
439 break;
440
441 case DDR_CMD_GO:
442 next_step = STEP_RUN;
443 break;
444
445 case DDR_CMD_NEXT:
446 next_step = step + 1;
447 break;
448
449 case DDR_CMD_STEP:
450 next_step = stm32mp1_do_step(step, argc, argv);
451 break;
452
Patrick Delaunay2b1c8182019-04-10 14:09:28 +0200453#ifdef CONFIG_STM32MP1_DDR_TESTS
454 case DDR_CMD_TEST:
455 if (!stm32mp1_check_step(step, STEP_DDR_READY))
456 continue;
457 stm32mp1_ddr_subcmd(priv, argc, argv, test, test_nb);
458 break;
459#endif
Patrick Delaunaybbee2702019-04-10 14:09:27 +0200460 default:
461 break;
462 }
463 }
464 return next_step == STEP_DDR_RESET;
465}