mirror of
				https://source.denx.de/u-boot/u-boot.git
				synced 2025-11-04 10:21:25 +01:00 
			
		
		
		
	Remove the DDR interactive command tuning, as the support of a predefined DDR PHY tuning is removed for STM32MP1 driver in SPL and in TF-A and the result of this tuning will be never used. Moreover this SW tuning procedure can failed on some hardware configuration (to many BIST errors and no convergence); it will be no more supported in the next delivery of the DDR utilities included in the CubeMX tool of STMicroelectronics. Signed-off-by: Patrick Delaunay <patrick.delaunay@foss.st.com> Reviewed-by: Patrice Chotard <patrice.chotard@foss.st.com>
		
			
				
	
	
		
			467 lines
		
	
	
		
			11 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			467 lines
		
	
	
		
			11 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
// SPDX-License-Identifier: GPL-2.0+ OR BSD-3-Clause
 | 
						|
/*
 | 
						|
 * Copyright (C) 2019, STMicroelectronics - All Rights Reserved
 | 
						|
 */
 | 
						|
 | 
						|
#define LOG_CATEGORY UCLASS_RAM
 | 
						|
 | 
						|
#include <common.h>
 | 
						|
#include <command.h>
 | 
						|
#include <console.h>
 | 
						|
#include <cli.h>
 | 
						|
#include <clk.h>
 | 
						|
#include <log.h>
 | 
						|
#include <malloc.h>
 | 
						|
#include <ram.h>
 | 
						|
#include <reset.h>
 | 
						|
#include <asm/global_data.h>
 | 
						|
#include "stm32mp1_ddr.h"
 | 
						|
#include "stm32mp1_tests.h"
 | 
						|
 | 
						|
DECLARE_GLOBAL_DATA_PTR;
 | 
						|
 | 
						|
enum ddr_command {
 | 
						|
	DDR_CMD_HELP,
 | 
						|
	DDR_CMD_INFO,
 | 
						|
	DDR_CMD_FREQ,
 | 
						|
	DDR_CMD_RESET,
 | 
						|
	DDR_CMD_PARAM,
 | 
						|
	DDR_CMD_PRINT,
 | 
						|
	DDR_CMD_EDIT,
 | 
						|
	DDR_CMD_STEP,
 | 
						|
	DDR_CMD_NEXT,
 | 
						|
	DDR_CMD_GO,
 | 
						|
	DDR_CMD_TEST,
 | 
						|
	DDR_CMD_UNKNOWN,
 | 
						|
};
 | 
						|
 | 
						|
const char *step_str[] = {
 | 
						|
	[STEP_DDR_RESET] = "DDR_RESET",
 | 
						|
	[STEP_CTL_INIT] = "DDR_CTRL_INIT_DONE",
 | 
						|
	[STEP_PHY_INIT] = "DDR PHY_INIT_DONE",
 | 
						|
	[STEP_DDR_READY] = "DDR_READY",
 | 
						|
	[STEP_RUN] = "RUN"
 | 
						|
};
 | 
						|
 | 
						|
enum ddr_command stm32mp1_get_command(char *cmd, int argc)
 | 
						|
{
 | 
						|
	const char *cmd_string[DDR_CMD_UNKNOWN] = {
 | 
						|
		[DDR_CMD_HELP] = "help",
 | 
						|
		[DDR_CMD_INFO] = "info",
 | 
						|
		[DDR_CMD_FREQ] = "freq",
 | 
						|
		[DDR_CMD_RESET] = "reset",
 | 
						|
		[DDR_CMD_PARAM] = "param",
 | 
						|
		[DDR_CMD_PRINT] = "print",
 | 
						|
		[DDR_CMD_EDIT] = "edit",
 | 
						|
		[DDR_CMD_STEP] = "step",
 | 
						|
		[DDR_CMD_NEXT] = "next",
 | 
						|
		[DDR_CMD_GO] = "go",
 | 
						|
#ifdef CONFIG_STM32MP1_DDR_TESTS
 | 
						|
		[DDR_CMD_TEST] = "test",
 | 
						|
#endif
 | 
						|
	};
 | 
						|
	/* min and max number of argument */
 | 
						|
	const char cmd_arg[DDR_CMD_UNKNOWN][2] = {
 | 
						|
		[DDR_CMD_HELP] = { 0, 0 },
 | 
						|
		[DDR_CMD_INFO] = { 0, 255 },
 | 
						|
		[DDR_CMD_FREQ] = { 0, 1 },
 | 
						|
		[DDR_CMD_RESET] = { 0, 0 },
 | 
						|
		[DDR_CMD_PARAM] = { 0, 2 },
 | 
						|
		[DDR_CMD_PRINT] = { 0, 1 },
 | 
						|
		[DDR_CMD_EDIT] = { 2, 2 },
 | 
						|
		[DDR_CMD_STEP] = { 0, 1 },
 | 
						|
		[DDR_CMD_NEXT] = { 0, 0 },
 | 
						|
		[DDR_CMD_GO] = { 0, 0 },
 | 
						|
#ifdef CONFIG_STM32MP1_DDR_TESTS
 | 
						|
		[DDR_CMD_TEST] = { 0, 255 },
 | 
						|
#endif
 | 
						|
	};
 | 
						|
	int i;
 | 
						|
 | 
						|
	for (i = 0; i < DDR_CMD_UNKNOWN; i++)
 | 
						|
		if (!strcmp(cmd, cmd_string[i])) {
 | 
						|
			if (argc - 1 < cmd_arg[i][0]) {
 | 
						|
				printf("no enought argument (min=%d)\n",
 | 
						|
				       cmd_arg[i][0]);
 | 
						|
				return DDR_CMD_UNKNOWN;
 | 
						|
			} else if (argc - 1 > cmd_arg[i][1]) {
 | 
						|
				printf("too many argument (max=%d)\n",
 | 
						|
				       cmd_arg[i][1]);
 | 
						|
				return DDR_CMD_UNKNOWN;
 | 
						|
			} else {
 | 
						|
				return i;
 | 
						|
			}
 | 
						|
		}
 | 
						|
 | 
						|
	printf("unknown command %s\n", cmd);
 | 
						|
	return DDR_CMD_UNKNOWN;
 | 
						|
}
 | 
						|
 | 
						|
static void stm32mp1_do_usage(void)
 | 
						|
{
 | 
						|
	const char *usage = {
 | 
						|
		"commands:\n\n"
 | 
						|
		"help                       displays help\n"
 | 
						|
		"info                       displays DDR information\n"
 | 
						|
		"info  <param> <val>        changes DDR information\n"
 | 
						|
		"      with <param> = step, name, size or speed\n"
 | 
						|
		"freq                       displays the DDR PHY frequency in kHz\n"
 | 
						|
		"freq  <freq>               changes the DDR PHY frequency\n"
 | 
						|
		"param [type|reg]           prints input parameters\n"
 | 
						|
		"param <reg> <val>          edits parameters in step 0\n"
 | 
						|
		"print [type|reg]           dumps registers\n"
 | 
						|
		"edit <reg> <val>           modifies one register\n"
 | 
						|
		"step                       lists the available step\n"
 | 
						|
		"step <n>                   go to the step <n>\n"
 | 
						|
		"next                       goes to the next step\n"
 | 
						|
		"go                         continues the U-Boot SPL execution\n"
 | 
						|
		"reset                      reboots machine\n"
 | 
						|
#ifdef CONFIG_STM32MP1_DDR_TESTS
 | 
						|
		"test [help] | <n> [...]    lists (with help) or executes test <n>\n"
 | 
						|
#endif
 | 
						|
		"\nwith for [type|reg]:\n"
 | 
						|
		"  all registers if absent\n"
 | 
						|
		"  <type> = ctl, phy\n"
 | 
						|
		"           or one category (static, timing, map, perf, dyn)\n"
 | 
						|
		"  <reg> = name of the register\n"
 | 
						|
	};
 | 
						|
 | 
						|
	puts(usage);
 | 
						|
}
 | 
						|
 | 
						|
static bool stm32mp1_check_step(enum stm32mp1_ddr_interact_step step,
 | 
						|
				enum stm32mp1_ddr_interact_step expected)
 | 
						|
{
 | 
						|
	if (step != expected) {
 | 
						|
		printf("invalid step %d:%s expecting %d:%s\n",
 | 
						|
		       step, step_str[step],
 | 
						|
		       expected,
 | 
						|
		       step_str[expected]);
 | 
						|
		return false;
 | 
						|
	}
 | 
						|
	return true;
 | 
						|
}
 | 
						|
 | 
						|
static void stm32mp1_do_info(struct ddr_info *priv,
 | 
						|
			     struct stm32mp1_ddr_config *config,
 | 
						|
			     enum stm32mp1_ddr_interact_step step,
 | 
						|
			     int argc, char *const argv[])
 | 
						|
{
 | 
						|
	unsigned long value;
 | 
						|
	static char *ddr_name;
 | 
						|
 | 
						|
	if (argc == 1) {
 | 
						|
		printf("step = %d : %s\n", step, step_str[step]);
 | 
						|
		printf("name = %s\n", config->info.name);
 | 
						|
		printf("size = 0x%x\n", config->info.size);
 | 
						|
		printf("speed = %d kHz\n", config->info.speed);
 | 
						|
		return;
 | 
						|
	}
 | 
						|
 | 
						|
	if (argc < 3) {
 | 
						|
		printf("no enought parameter\n");
 | 
						|
		return;
 | 
						|
	}
 | 
						|
	if (!strcmp(argv[1], "name")) {
 | 
						|
		u32 i, name_len = 0;
 | 
						|
 | 
						|
		for (i = 2; i < argc; i++)
 | 
						|
			name_len += strlen(argv[i]) + 1;
 | 
						|
		if (ddr_name)
 | 
						|
			free(ddr_name);
 | 
						|
		ddr_name = malloc(name_len);
 | 
						|
		config->info.name = ddr_name;
 | 
						|
		if (!ddr_name) {
 | 
						|
			printf("alloc error, length %d\n", name_len);
 | 
						|
			return;
 | 
						|
		}
 | 
						|
		strcpy(ddr_name, argv[2]);
 | 
						|
		for (i = 3; i < argc; i++) {
 | 
						|
			strcat(ddr_name, " ");
 | 
						|
			strcat(ddr_name, argv[i]);
 | 
						|
		}
 | 
						|
		printf("name = %s\n", ddr_name);
 | 
						|
		return;
 | 
						|
	}
 | 
						|
	if (!strcmp(argv[1], "size")) {
 | 
						|
		if (strict_strtoul(argv[2], 16, &value) < 0) {
 | 
						|
			printf("invalid value %s\n", argv[2]);
 | 
						|
		} else {
 | 
						|
			config->info.size = value;
 | 
						|
			printf("size = 0x%x\n", config->info.size);
 | 
						|
		}
 | 
						|
		return;
 | 
						|
	}
 | 
						|
	if (!strcmp(argv[1], "speed")) {
 | 
						|
		if (strict_strtoul(argv[2], 10, &value) < 0) {
 | 
						|
			printf("invalid value %s\n", argv[2]);
 | 
						|
		} else {
 | 
						|
			config->info.speed = value;
 | 
						|
			printf("speed = %d kHz\n", config->info.speed);
 | 
						|
			value = clk_get_rate(&priv->clk);
 | 
						|
			printf("DDRPHY = %ld kHz\n", value / 1000);
 | 
						|
		}
 | 
						|
		return;
 | 
						|
	}
 | 
						|
	printf("argument %s invalid\n", argv[1]);
 | 
						|
}
 | 
						|
 | 
						|
static bool stm32mp1_do_freq(struct ddr_info *priv,
 | 
						|
			     int argc, char *const argv[])
 | 
						|
{
 | 
						|
	unsigned long ddrphy_clk;
 | 
						|
 | 
						|
	if (argc == 2) {
 | 
						|
		if (strict_strtoul(argv[1], 0, &ddrphy_clk) < 0) {
 | 
						|
			printf("invalid argument %s", argv[1]);
 | 
						|
			return false;
 | 
						|
		}
 | 
						|
		if (clk_set_rate(&priv->clk, ddrphy_clk * 1000)) {
 | 
						|
			printf("ERROR: update failed!\n");
 | 
						|
			return false;
 | 
						|
		}
 | 
						|
	}
 | 
						|
	ddrphy_clk = clk_get_rate(&priv->clk);
 | 
						|
	printf("DDRPHY = %ld kHz\n", ddrphy_clk / 1000);
 | 
						|
	if (argc == 2)
 | 
						|
		return true;
 | 
						|
	return false;
 | 
						|
}
 | 
						|
 | 
						|
static void stm32mp1_do_param(enum stm32mp1_ddr_interact_step step,
 | 
						|
			      const struct stm32mp1_ddr_config *config,
 | 
						|
			      int argc, char *const argv[])
 | 
						|
{
 | 
						|
	switch (argc) {
 | 
						|
	case 1:
 | 
						|
		stm32mp1_dump_param(config, NULL);
 | 
						|
		break;
 | 
						|
	case 2:
 | 
						|
		if (stm32mp1_dump_param(config, argv[1]))
 | 
						|
			printf("invalid argument %s\n",
 | 
						|
			       argv[1]);
 | 
						|
		break;
 | 
						|
	case 3:
 | 
						|
		if (!stm32mp1_check_step(step, STEP_DDR_RESET))
 | 
						|
			return;
 | 
						|
		stm32mp1_edit_param(config, argv[1], argv[2]);
 | 
						|
		break;
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
static void stm32mp1_do_print(struct ddr_info *priv,
 | 
						|
			      int argc, char *const argv[])
 | 
						|
{
 | 
						|
	switch (argc) {
 | 
						|
	case 1:
 | 
						|
		stm32mp1_dump_reg(priv, NULL);
 | 
						|
		break;
 | 
						|
	case 2:
 | 
						|
		if (stm32mp1_dump_reg(priv, argv[1]))
 | 
						|
			printf("invalid argument %s\n",
 | 
						|
			       argv[1]);
 | 
						|
		break;
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
static int stm32mp1_do_step(enum stm32mp1_ddr_interact_step step,
 | 
						|
			    int argc, char *const argv[])
 | 
						|
{
 | 
						|
	int i;
 | 
						|
	unsigned long value;
 | 
						|
 | 
						|
	switch (argc) {
 | 
						|
	case 1:
 | 
						|
		for (i = 0; i < ARRAY_SIZE(step_str); i++)
 | 
						|
			printf("%d:%s\n", i, step_str[i]);
 | 
						|
		break;
 | 
						|
 | 
						|
	case 2:
 | 
						|
		if ((strict_strtoul(argv[1], 0,
 | 
						|
				    &value) < 0) ||
 | 
						|
				    value >= ARRAY_SIZE(step_str)) {
 | 
						|
			printf("invalid argument %s\n",
 | 
						|
			       argv[1]);
 | 
						|
			goto end;
 | 
						|
		}
 | 
						|
 | 
						|
		if (value != STEP_DDR_RESET &&
 | 
						|
		    value <= step) {
 | 
						|
			printf("invalid target %d:%s, current step is %d:%s\n",
 | 
						|
			       (int)value, step_str[value],
 | 
						|
			       step, step_str[step]);
 | 
						|
			goto end;
 | 
						|
		}
 | 
						|
		printf("step to %d:%s\n",
 | 
						|
		       (int)value, step_str[value]);
 | 
						|
		return (int)value;
 | 
						|
	};
 | 
						|
 | 
						|
end:
 | 
						|
	return step;
 | 
						|
}
 | 
						|
 | 
						|
#if defined(CONFIG_STM32MP1_DDR_TESTS)
 | 
						|
static const char * const s_result[] = {
 | 
						|
		[TEST_PASSED] = "Pass",
 | 
						|
		[TEST_FAILED] = "Failed",
 | 
						|
		[TEST_ERROR] = "Error"
 | 
						|
};
 | 
						|
 | 
						|
static void stm32mp1_ddr_subcmd(struct ddr_info *priv,
 | 
						|
				int argc, char *argv[],
 | 
						|
				const struct test_desc array[],
 | 
						|
				const int array_nb)
 | 
						|
{
 | 
						|
	int i;
 | 
						|
	unsigned long value;
 | 
						|
	int result;
 | 
						|
	char string[50] = "";
 | 
						|
 | 
						|
	if (argc == 1) {
 | 
						|
		printf("%s:%d\n", argv[0], array_nb);
 | 
						|
		for (i = 0; i < array_nb; i++)
 | 
						|
			printf("%d:%s:%s\n",
 | 
						|
			       i, array[i].name, array[i].usage);
 | 
						|
		return;
 | 
						|
	}
 | 
						|
	if (argc > 1 && !strcmp(argv[1], "help")) {
 | 
						|
		printf("%s:%d\n", argv[0], array_nb);
 | 
						|
		for (i = 0; i < array_nb; i++)
 | 
						|
			printf("%d:%s:%s:%s\n", i,
 | 
						|
			       array[i].name, array[i].usage, array[i].help);
 | 
						|
		return;
 | 
						|
	}
 | 
						|
 | 
						|
	if ((strict_strtoul(argv[1], 0, &value) <  0) ||
 | 
						|
	    value >= array_nb) {
 | 
						|
		sprintf(string, "invalid argument %s",
 | 
						|
			argv[1]);
 | 
						|
		result = TEST_FAILED;
 | 
						|
		goto end;
 | 
						|
	}
 | 
						|
 | 
						|
	if (argc > (array[value].max_args + 2)) {
 | 
						|
		sprintf(string, "invalid nb of args %d, max %d",
 | 
						|
			argc - 2, array[value].max_args);
 | 
						|
		result = TEST_FAILED;
 | 
						|
		goto end;
 | 
						|
	}
 | 
						|
 | 
						|
	printf("execute %d:%s\n", (int)value, array[value].name);
 | 
						|
	clear_ctrlc();
 | 
						|
	result = array[value].fct(priv->ctl, priv->phy,
 | 
						|
				  string, argc - 2, &argv[2]);
 | 
						|
 | 
						|
end:
 | 
						|
	printf("Result: %s [%s]\n", s_result[result], string);
 | 
						|
}
 | 
						|
#endif
 | 
						|
 | 
						|
bool stm32mp1_ddr_interactive(void *priv,
 | 
						|
			      enum stm32mp1_ddr_interact_step step,
 | 
						|
			      const struct stm32mp1_ddr_config *config)
 | 
						|
{
 | 
						|
	char buffer[CONFIG_SYS_CBSIZE];
 | 
						|
	char *argv[CONFIG_SYS_MAXARGS + 1];	/* NULL terminated */
 | 
						|
	int argc;
 | 
						|
	static int next_step = -1;
 | 
						|
 | 
						|
	if (next_step < 0 && step == STEP_DDR_RESET) {
 | 
						|
#ifdef CONFIG_STM32MP1_DDR_INTERACTIVE_FORCE
 | 
						|
		gd->flags &= ~(GD_FLG_SILENT |
 | 
						|
			       GD_FLG_DISABLE_CONSOLE);
 | 
						|
		next_step = STEP_DDR_RESET;
 | 
						|
#else
 | 
						|
		unsigned long start = get_timer(0);
 | 
						|
 | 
						|
		while (1) {
 | 
						|
			if (tstc() && (getchar() == 'd')) {
 | 
						|
				next_step = STEP_DDR_RESET;
 | 
						|
				break;
 | 
						|
			}
 | 
						|
			if (get_timer(start) > 100)
 | 
						|
				break;
 | 
						|
		}
 | 
						|
#endif
 | 
						|
	}
 | 
						|
 | 
						|
	log_debug("** step %d ** %s / %d\n", step, step_str[step], next_step);
 | 
						|
 | 
						|
	if (next_step < 0)
 | 
						|
		return false;
 | 
						|
 | 
						|
	if (step < 0 || step > ARRAY_SIZE(step_str)) {
 | 
						|
		printf("** step %d ** INVALID\n", step);
 | 
						|
		return false;
 | 
						|
	}
 | 
						|
 | 
						|
	printf("%d:%s\n", step, step_str[step]);
 | 
						|
 | 
						|
	if (next_step > step)
 | 
						|
		return false;
 | 
						|
 | 
						|
	while (next_step == step) {
 | 
						|
		cli_readline_into_buffer("DDR>", buffer, 0);
 | 
						|
		argc = cli_simple_parse_line(buffer, argv);
 | 
						|
		if (!argc)
 | 
						|
			continue;
 | 
						|
 | 
						|
		switch (stm32mp1_get_command(argv[0], argc)) {
 | 
						|
		case DDR_CMD_HELP:
 | 
						|
			stm32mp1_do_usage();
 | 
						|
			break;
 | 
						|
 | 
						|
		case DDR_CMD_INFO:
 | 
						|
			stm32mp1_do_info(priv,
 | 
						|
					 (struct stm32mp1_ddr_config *)config,
 | 
						|
					 step, argc, argv);
 | 
						|
			break;
 | 
						|
 | 
						|
		case DDR_CMD_FREQ:
 | 
						|
			if (stm32mp1_do_freq(priv, argc, argv))
 | 
						|
				next_step = STEP_DDR_RESET;
 | 
						|
			break;
 | 
						|
 | 
						|
		case DDR_CMD_RESET:
 | 
						|
			do_reset(NULL, 0, 0, NULL);
 | 
						|
			break;
 | 
						|
 | 
						|
		case DDR_CMD_PARAM:
 | 
						|
			stm32mp1_do_param(step, config, argc, argv);
 | 
						|
			break;
 | 
						|
 | 
						|
		case DDR_CMD_PRINT:
 | 
						|
			stm32mp1_do_print(priv, argc, argv);
 | 
						|
			break;
 | 
						|
 | 
						|
		case DDR_CMD_EDIT:
 | 
						|
			stm32mp1_edit_reg(priv, argv[1], argv[2]);
 | 
						|
			break;
 | 
						|
 | 
						|
		case DDR_CMD_GO:
 | 
						|
			next_step = STEP_RUN;
 | 
						|
			break;
 | 
						|
 | 
						|
		case DDR_CMD_NEXT:
 | 
						|
			next_step = step + 1;
 | 
						|
			break;
 | 
						|
 | 
						|
		case DDR_CMD_STEP:
 | 
						|
			next_step = stm32mp1_do_step(step, argc, argv);
 | 
						|
			break;
 | 
						|
 | 
						|
#ifdef CONFIG_STM32MP1_DDR_TESTS
 | 
						|
		case DDR_CMD_TEST:
 | 
						|
			if (!stm32mp1_check_step(step, STEP_DDR_READY))
 | 
						|
				continue;
 | 
						|
			stm32mp1_ddr_subcmd(priv, argc, argv, test, test_nb);
 | 
						|
			break;
 | 
						|
#endif
 | 
						|
		default:
 | 
						|
			break;
 | 
						|
		}
 | 
						|
	}
 | 
						|
	return next_step == STEP_DDR_RESET;
 | 
						|
}
 |