]>
Commit | Line | Data |
---|---|---|
01a75108 PD |
1 | // 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> | |
09140113 | 7 | #include <command.h> |
01a75108 PD |
8 | #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" | |
0d447524 | 15 | #include "stm32mp1_tests.h" |
01a75108 PD |
16 | |
17 | DECLARE_GLOBAL_DATA_PTR; | |
18 | ||
19 | enum 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 | ||
35 | const 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 | ||
43 | enum 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", | |
0d447524 PD |
56 | #ifdef CONFIG_STM32MP1_DDR_TESTS |
57 | [DDR_CMD_TEST] = "test", | |
187c41d7 PD |
58 | #endif |
59 | #ifdef CONFIG_STM32MP1_DDR_TUNING | |
60 | [DDR_CMD_TUNING] = "tuning", | |
0d447524 | 61 | #endif |
01a75108 PD |
62 | }; |
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 }, | |
0d447524 PD |
75 | #ifdef CONFIG_STM32MP1_DDR_TESTS |
76 | [DDR_CMD_TEST] = { 0, 255 }, | |
187c41d7 PD |
77 | #endif |
78 | #ifdef CONFIG_STM32MP1_DDR_TUNING | |
79 | [DDR_CMD_TUNING] = { 0, 255 }, | |
0d447524 | 80 | #endif |
01a75108 PD |
81 | }; |
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 | ||
103 | static 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" | |
9368bdfe | 110 | " with <param> = step, name, size, speed or cal\n" |
01a75108 PD |
111 | "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" | |
0d447524 PD |
122 | #ifdef CONFIG_STM32MP1_DDR_TESTS |
123 | "test [help] | <n> [...] lists (with help) or executes test <n>\n" | |
187c41d7 PD |
124 | #endif |
125 | #ifdef CONFIG_STM32MP1_DDR_TUNING | |
126 | "tuning [help] | <n> [...] lists (with help) or execute tuning <n>\n" | |
0d447524 | 127 | #endif |
01a75108 PD |
128 | "\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 | ||
138 | static 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 | ||
151 | static void stm32mp1_do_info(struct ddr_info *priv, | |
152 | struct stm32mp1_ddr_config *config, | |
153 | enum stm32mp1_ddr_interact_step step, | |
09140113 | 154 | int argc, char *const argv[]) |
01a75108 PD |
155 | { |
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); | |
9368bdfe | 164 | printf("cal = %d\n", config->p_cal_present); |
01a75108 PD |
165 | 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 | } | |
9368bdfe PD |
213 | 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 | } | |
01a75108 PD |
223 | printf("argument %s invalid\n", argv[1]); |
224 | } | |
225 | ||
226 | static bool stm32mp1_do_freq(struct ddr_info *priv, | |
09140113 | 227 | int argc, char *const argv[]) |
01a75108 PD |
228 | { |
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 | ||
248 | static void stm32mp1_do_param(enum stm32mp1_ddr_interact_step step, | |
249 | const struct stm32mp1_ddr_config *config, | |
09140113 | 250 | int argc, char *const argv[]) |
01a75108 PD |
251 | { |
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 | ||
269 | static void stm32mp1_do_print(struct ddr_info *priv, | |
09140113 | 270 | int argc, char *const argv[]) |
01a75108 PD |
271 | { |
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 | ||
284 | static int stm32mp1_do_step(enum stm32mp1_ddr_interact_step step, | |
09140113 | 285 | int argc, char *const argv[]) |
01a75108 PD |
286 | { |
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 | ||
317 | end: | |
318 | return step; | |
319 | } | |
320 | ||
187c41d7 | 321 | #if defined(CONFIG_STM32MP1_DDR_TESTS) || defined(CONFIG_STM32MP1_DDR_TUNING) |
0d447524 PD |
322 | static const char * const s_result[] = { |
323 | [TEST_PASSED] = "Pass", | |
324 | [TEST_FAILED] = "Failed", | |
325 | [TEST_ERROR] = "Error" | |
326 | }; | |
327 | ||
328 | static 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 | ||
373 | end: | |
374 | printf("Result: %s [%s]\n", s_result[result], string); | |
375 | } | |
376 | #endif | |
377 | ||
01a75108 PD |
378 | bool stm32mp1_ddr_interactive(void *priv, |
379 | enum stm32mp1_ddr_interact_step step, | |
380 | const struct stm32mp1_ddr_config *config) | |
381 | { | |
01a75108 PD |
382 | 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]); | |
01a75108 PD |
417 | |
418 | if (next_step > step) | |
419 | return false; | |
420 | ||
421 | while (next_step == step) { | |
1c55a91b | 422 | cli_readline_into_buffer("DDR>", buffer, 0); |
01a75108 PD |
423 | 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 | ||
0d447524 PD |
471 | #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 | ||
187c41d7 PD |
479 | #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 | ||
01a75108 PD |
488 | default: |
489 | break; | |
490 | } | |
491 | } | |
492 | return next_step == STEP_DDR_RESET; | |
493 | } |