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 <asm/global_data.h>
18#include "stm32mp1_ddr.h"
19#include "stm32mp1_tests.h"
20
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,
35	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",
59#ifdef CONFIG_STM32MP1_DDR_TESTS
60		[DDR_CMD_TEST] = "test",
61#endif
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 },
75#ifdef CONFIG_STM32MP1_DDR_TESTS
76		[DDR_CMD_TEST] = { 0, 255 },
77#endif
78	};
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"
107		"      with <param> = step, name, size or speed\n"
108		"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"
119#ifdef CONFIG_STM32MP1_DDR_TESTS
120		"test [help] | <n> [...]    lists (with help) or executes test <n>\n"
121#endif
122		"\nwith for [type|reg]:\n"
123		"  all registers if absent\n"
124		"  <type> = ctl, phy\n"
125		"           or one category (static, timing, map, perf, dyn)\n"
126		"  <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,
148			     int argc, char *const argv[])
149{
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,
210			     int argc, char *const argv[])
211{
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,
233			      int argc, char *const argv[])
234{
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,
253			      int argc, char *const argv[])
254{
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,
268			    int argc, char *const argv[])
269{
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
304#if defined(CONFIG_STM32MP1_DDR_TESTS)
305static 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
361bool stm32mp1_ddr_interactive(void *priv,
362			      enum stm32mp1_ddr_interact_step step,
363			      const struct stm32mp1_ddr_config *config)
364{
365	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) {
379			if (tstc() && (getchar() == 'd')) {
380				next_step = STEP_DDR_RESET;
381				break;
382			}
383			if (get_timer(start) > 100)
384				break;
385		}
386#endif
387	}
388
389	log_debug("** step %d ** %s / %d\n", step, step_str[step], next_step);
390
391	if (next_step < 0)
392		return false;
393
394	if (step < 0 || step >= ARRAY_SIZE(step_str)) {
395		printf("** step %d ** INVALID\n", step);
396		return false;
397	}
398
399	printf("%d:%s\n", step, step_str[step]);
400
401	if (next_step > step)
402		return false;
403
404	while (next_step == step) {
405		cli_readline_into_buffer("DDR>", buffer, 0);
406		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
454#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
461		default:
462			break;
463		}
464	}
465	return next_step == STEP_DDR_RESET;
466}
467