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