[U-Boot] [PATCH 7/9] stm32mp1: ram: add interactive mode for DDR configuration

Patrick Delaunay patrick.delaunay at st.com
Wed Apr 10 12:09:27 UTC 2019


This debug mode is used by CubeMX DDR tuning tools
or manualy for tests during board bring-up.
It is simple console used to change DDR parameters and check
initialization.

Signed-off-by: Patrick Delaunay <patrick.delaunay at st.com>
---

 common/Makefile                             |   1 +
 drivers/ram/stm32mp1/Kconfig                |  19 ++
 drivers/ram/stm32mp1/Makefile               |   6 +
 drivers/ram/stm32mp1/stm32mp1_ddr.c         | 298 +++++++++++++++++++++
 drivers/ram/stm32mp1/stm32mp1_interactive.c | 390 ++++++++++++++++++++++++++++
 5 files changed, 714 insertions(+)
 create mode 100644 drivers/ram/stm32mp1/stm32mp1_interactive.c

diff --git a/common/Makefile b/common/Makefile
index 8c92feb..c7e41ef 100644
--- a/common/Makefile
+++ b/common/Makefile
@@ -124,6 +124,7 @@ endif
 
 obj-y += cli.o
 obj-$(CONFIG_FSL_DDR_INTERACTIVE) += cli_simple.o cli_readline.o
+obj-$(CONFIG_STM32MP1_DDR_INTERACTIVE) += cli_simple.o cli_readline.o
 obj-$(CONFIG_DFU_OVER_USB) += dfu.o
 obj-y += command.o
 obj-$(CONFIG_$(SPL_TPL_)LOG) += log.o
diff --git a/drivers/ram/stm32mp1/Kconfig b/drivers/ram/stm32mp1/Kconfig
index b9c8166..511eaf2 100644
--- a/drivers/ram/stm32mp1/Kconfig
+++ b/drivers/ram/stm32mp1/Kconfig
@@ -10,3 +10,22 @@ config STM32MP1_DDR
 		family:	support for LPDDR2, LPDDR3 and DDR3
 		the SDRAM parameters for controleur and phy need to be provided
 		in device tree (computed by DDR tuning tools)
+
+config STM32MP1_DDR_INTERACTIVE
+	bool "STM32MP1 DDR driver : interactive support"
+	depends on STM32MP1_DDR
+	help
+		activate interactive support in STM32MP1 DDR controller driver
+		used for DDR tuning tools
+		to enter in intercative mode type 'd' during SPL DDR driver
+		initialisation
+
+config STM32MP1_DDR_INTERACTIVE_FORCE
+	bool "STM32MP1 DDR driver : force interactive mode"
+	depends on STM32MP1_DDR_INTERACTIVE
+	default n
+	help
+		force interactive mode in STM32MP1 DDR controller driver
+		skip the polling of character 'd' in console
+		useful when SPL is loaded in sysram
+		directly by programmer
diff --git a/drivers/ram/stm32mp1/Makefile b/drivers/ram/stm32mp1/Makefile
index 79eb028..7b1f095 100644
--- a/drivers/ram/stm32mp1/Makefile
+++ b/drivers/ram/stm32mp1/Makefile
@@ -5,3 +5,9 @@
 
 obj-y += stm32mp1_ram.o
 obj-y += stm32mp1_ddr.o
+
+obj-$(CONFIG_STM32MP1_DDR_INTERACTIVE) += stm32mp1_interactive.o
+
+ifneq ($(DDR_INTERACTIVE),)
+CFLAGS_stm32mp1_interactive.o += -DCONFIG_STM32MP1_DDR_INTERACTIVE_FORCE=y
+endif
diff --git a/drivers/ram/stm32mp1/stm32mp1_ddr.c b/drivers/ram/stm32mp1/stm32mp1_ddr.c
index cfd223e..d765a46 100644
--- a/drivers/ram/stm32mp1/stm32mp1_ddr.c
+++ b/drivers/ram/stm32mp1/stm32mp1_ddr.c
@@ -41,6 +41,16 @@ struct reg_desc {
 	 offsetof(struct stm32mp1_ddrphy, x),\
 	 offsetof(struct y, x)}
 
+#define DDR_REG_DYN(x) \
+	{#x,\
+	 offsetof(struct stm32mp1_ddrctl, x),\
+	 INVALID_OFFSET}
+
+#define DDRPHY_REG_DYN(x) \
+	{#x,\
+	 offsetof(struct stm32mp1_ddrphy, x),\
+	 INVALID_OFFSET}
+
 /***********************************************************
  * PARAMETERS: value get from device tree :
  *             size / order need to be aligned with binding
@@ -179,6 +189,42 @@ static const struct reg_desc ddrphy_cal[DDRPHY_REG_CAL_SIZE] = {
 	DDRPHY_REG_CAL(dx3dqstr),
 };
 
+/**************************************************************
+ * DYNAMIC REGISTERS: only used for debug purpose (read/modify)
+ **************************************************************/
+#ifdef CONFIG_STM32MP1_DDR_INTERACTIVE
+static const struct reg_desc ddr_dyn[] = {
+	DDR_REG_DYN(stat),
+	DDR_REG_DYN(init0),
+	DDR_REG_DYN(dfimisc),
+	DDR_REG_DYN(dfistat),
+	DDR_REG_DYN(swctl),
+	DDR_REG_DYN(swstat),
+	DDR_REG_DYN(pctrl_0),
+	DDR_REG_DYN(pctrl_1),
+};
+
+#define DDR_REG_DYN_SIZE	ARRAY_SIZE(ddr_dyn)
+
+static const struct reg_desc ddrphy_dyn[] = {
+	DDRPHY_REG_DYN(pir),
+	DDRPHY_REG_DYN(pgsr),
+	DDRPHY_REG_DYN(zq0sr0),
+	DDRPHY_REG_DYN(zq0sr1),
+	DDRPHY_REG_DYN(dx0gsr0),
+	DDRPHY_REG_DYN(dx0gsr1),
+	DDRPHY_REG_DYN(dx1gsr0),
+	DDRPHY_REG_DYN(dx1gsr1),
+	DDRPHY_REG_DYN(dx2gsr0),
+	DDRPHY_REG_DYN(dx2gsr1),
+	DDRPHY_REG_DYN(dx3gsr0),
+	DDRPHY_REG_DYN(dx3gsr1),
+};
+
+#define DDRPHY_REG_DYN_SIZE	ARRAY_SIZE(ddrphy_dyn)
+
+#endif
+
 /*****************************************************************
  * REGISTERS ARRAY: used to parse device tree and interactive mode
  *****************************************************************/
@@ -190,6 +236,13 @@ enum reg_type {
 	REGPHY_REG,
 	REGPHY_TIMING,
 	REGPHY_CAL,
+#ifdef CONFIG_STM32MP1_DDR_INTERACTIVE
+/* dynamic registers => managed in driver or not changed,
+ * can be dumped in interactive mode
+ */
+	REG_DYN,
+	REGPHY_DYN,
+#endif
 	REG_TYPE_NB
 };
 
@@ -223,6 +276,13 @@ const struct ddr_reg_info ddr_registers[REG_TYPE_NB] = {
 	"timing", ddrphy_timing, DDRPHY_REG_TIMING_SIZE, DDRPHY_BASE},
 [REGPHY_CAL] = {
 	"cal", ddrphy_cal, DDRPHY_REG_CAL_SIZE, DDRPHY_BASE},
+#ifdef CONFIG_STM32MP1_DDR_INTERACTIVE
+[REG_DYN] = {
+	"dyn", ddr_dyn, DDR_REG_DYN_SIZE, DDR_BASE},
+[REGPHY_DYN] = {
+	"dyn", ddrphy_dyn, DDRPHY_REG_DYN_SIZE, DDRPHY_BASE},
+#endif
+
 };
 
 const char *base_name[] = {
@@ -263,6 +323,231 @@ static void set_reg(const struct ddr_info *priv,
 	}
 }
 
+#ifdef CONFIG_STM32MP1_DDR_INTERACTIVE
+static void stm32mp1_dump_reg_desc(u32 base_addr, const struct reg_desc *desc)
+{
+	unsigned int *ptr;
+
+	ptr = (unsigned int *)(base_addr + desc->offset);
+	printf("%s= 0x%08x\n", desc->name, readl(ptr));
+}
+
+static void stm32mp1_dump_param_desc(u32 par_addr, const struct reg_desc *desc)
+{
+	unsigned int *ptr;
+
+	ptr = (unsigned int *)(par_addr + desc->par_offset);
+	printf("%s= 0x%08x\n", desc->name, readl(ptr));
+}
+
+static const struct reg_desc *found_reg(const char *name, enum reg_type *type)
+{
+	unsigned int i, j;
+	const struct reg_desc *desc;
+
+	for (i = 0; i < ARRAY_SIZE(ddr_registers); i++) {
+		desc = ddr_registers[i].desc;
+		for (j = 0; j < ddr_registers[i].size; j++) {
+			if (strcmp(name, desc[j].name) == 0) {
+				*type = i;
+				return &desc[j];
+			}
+		}
+	}
+	*type = REG_TYPE_NB;
+	return NULL;
+}
+
+int stm32mp1_dump_reg(const struct ddr_info *priv,
+		      const char *name)
+{
+	unsigned int i, j;
+	const struct reg_desc *desc;
+	u32 base_addr;
+	enum base_type p_base;
+	enum reg_type type;
+	const char *p_name;
+	enum base_type filter = NONE_BASE;
+	int result = -1;
+
+	if (name) {
+		if (strcmp(name, base_name[DDR_BASE]) == 0)
+			filter = DDR_BASE;
+		else if (strcmp(name, base_name[DDRPHY_BASE]) == 0)
+			filter = DDRPHY_BASE;
+	}
+
+	for (i = 0; i < ARRAY_SIZE(ddr_registers); i++) {
+		p_base = ddr_registers[i].base;
+		p_name = ddr_registers[i].name;
+		if (!name || (filter == p_base || !strcmp(name, p_name))) {
+			result = 0;
+			desc = ddr_registers[i].desc;
+			base_addr = get_base_addr(priv, p_base);
+			printf("==%s.%s==\n", base_name[p_base], p_name);
+			for (j = 0; j < ddr_registers[i].size; j++)
+				stm32mp1_dump_reg_desc(base_addr, &desc[j]);
+		}
+	}
+	if (result) {
+		desc = found_reg(name, &type);
+		if (desc) {
+			p_base = ddr_registers[type].base;
+			base_addr = get_base_addr(priv, p_base);
+			stm32mp1_dump_reg_desc(base_addr, desc);
+			result = 0;
+		}
+	}
+	return result;
+}
+
+void stm32mp1_edit_reg(const struct ddr_info *priv,
+		       char *name, char *string)
+{
+	unsigned long *ptr, value;
+	enum reg_type type;
+	enum base_type base;
+	const struct reg_desc *desc;
+	u32 base_addr;
+
+	desc = found_reg(name, &type);
+
+	if (!desc) {
+		printf("%s not found\n", name);
+		return;
+	}
+	if (strict_strtoul(string, 16, &value) < 0) {
+		printf("invalid value %s\n", string);
+		return;
+	}
+	base = ddr_registers[type].base;
+	base_addr = get_base_addr(priv, base);
+	ptr = (unsigned long *)(base_addr + desc->offset);
+	writel(value, ptr);
+	printf("%s= 0x%08x\n", desc->name, readl(ptr));
+}
+
+static u32 get_par_addr(const struct stm32mp1_ddr_config *config,
+			enum reg_type type)
+{
+	u32 par_addr = 0x0;
+
+	switch (type) {
+	case REG_REG:
+		par_addr = (u32)&config->c_reg;
+		break;
+	case REG_TIMING:
+		par_addr = (u32)&config->c_timing;
+		break;
+	case REG_PERF:
+		par_addr = (u32)&config->c_perf;
+		break;
+	case REG_MAP:
+		par_addr = (u32)&config->c_map;
+		break;
+	case REGPHY_REG:
+		par_addr = (u32)&config->p_reg;
+		break;
+	case REGPHY_TIMING:
+		par_addr = (u32)&config->p_timing;
+		break;
+	case REGPHY_CAL:
+		par_addr = (u32)&config->p_cal;
+		break;
+	case REG_DYN:
+	case REGPHY_DYN:
+	case REG_TYPE_NB:
+		par_addr = (u32)NULL;
+		break;
+	}
+
+	return par_addr;
+}
+
+int stm32mp1_dump_param(const struct stm32mp1_ddr_config *config,
+			const char *name)
+{
+	unsigned int i, j;
+	const struct reg_desc *desc;
+	u32 par_addr;
+	enum base_type p_base;
+	enum reg_type type;
+	const char *p_name;
+	enum base_type filter = NONE_BASE;
+	int result = -EINVAL;
+
+	if (name) {
+		if (strcmp(name, base_name[DDR_BASE]) == 0)
+			filter = DDR_BASE;
+		else if (strcmp(name, base_name[DDRPHY_BASE]) == 0)
+			filter = DDRPHY_BASE;
+	}
+
+	for (i = 0; i < ARRAY_SIZE(ddr_registers); i++) {
+		par_addr = get_par_addr(config, i);
+		if (!par_addr)
+			continue;
+		p_base = ddr_registers[i].base;
+		p_name = ddr_registers[i].name;
+		if (!name || (filter == p_base || !strcmp(name, p_name))) {
+			result = 0;
+			desc = ddr_registers[i].desc;
+			printf("==%s.%s==\n", base_name[p_base], p_name);
+			for (j = 0; j < ddr_registers[i].size; j++)
+				stm32mp1_dump_param_desc(par_addr, &desc[j]);
+		}
+	}
+	if (result) {
+		desc = found_reg(name, &type);
+		if (desc) {
+			par_addr = get_par_addr(config, type);
+			if (par_addr) {
+				stm32mp1_dump_param_desc(par_addr, desc);
+				result = 0;
+			}
+		}
+	}
+	return result;
+}
+
+void stm32mp1_edit_param(const struct stm32mp1_ddr_config *config,
+			 char *name, char *string)
+{
+	unsigned long *ptr, value;
+	enum reg_type type;
+	const struct reg_desc *desc;
+	u32 par_addr;
+
+	desc = found_reg(name, &type);
+	if (!desc) {
+		printf("%s not found\n", name);
+		return;
+	}
+	if (strict_strtoul(string, 16, &value) < 0) {
+		printf("invalid value %s\n", string);
+		return;
+	}
+	par_addr = get_par_addr(config, type);
+	if (!par_addr) {
+		printf("no parameter %s\n", name);
+		return;
+	}
+	ptr = (unsigned long *)(par_addr + desc->par_offset);
+	writel(value, ptr);
+	printf("%s= 0x%08x\n", desc->name, readl(ptr));
+}
+#endif
+
+__weak bool stm32mp1_ddr_interactive(void *priv,
+				     enum stm32mp1_ddr_interact_step step,
+				     const struct stm32mp1_ddr_config *config)
+{
+	return false;
+}
+
+#define INTERACTIVE(step)\
+	stm32mp1_ddr_interactive(priv, step, config)
+
 static void ddrphy_idone_wait(struct stm32mp1_ddrphy *phy)
 {
 	u32 pgsr;
@@ -394,6 +679,7 @@ void stm32mp1_ddr_init(struct ddr_info *priv,
 	if (ret)
 		panic("ddr power init failed\n");
 
+start:
 	debug("name = %s\n", config->info.name);
 	debug("speed = %d kHz\n", config->info.speed);
 	debug("size  = 0x%x\n", config->info.size);
@@ -427,6 +713,9 @@ void stm32mp1_ddr_init(struct ddr_info *priv,
 	udelay(2);
 	/* for PCLK = 133MHz => 1 us is enough, 2 to allow lower frequency */
 
+	if (INTERACTIVE(STEP_DDR_RESET))
+		goto start;
+
 /* 1.5. initialize registers ddr_umctl2 */
 	/* Stop uMCTL2 before PHY is ready */
 	clrbits_le32(&priv->ctl->dfimisc, DDRCTRL_DFIMISC_DFI_INIT_COMPLETE_EN);
@@ -444,6 +733,9 @@ void stm32mp1_ddr_init(struct ddr_info *priv,
 
 	set_reg(priv, REG_PERF, &config->c_perf);
 
+	if (INTERACTIVE(STEP_CTL_INIT))
+		goto start;
+
 /*  2. deassert reset signal core_ddrc_rstn, aresetn and presetn */
 	clrbits_le32(priv->rcc + RCC_DDRITFCR, RCC_DDRITFCR_DDRCORERST);
 	clrbits_le32(priv->rcc + RCC_DDRITFCR, RCC_DDRITFCR_DDRCAXIRST);
@@ -456,6 +748,9 @@ void stm32mp1_ddr_init(struct ddr_info *priv,
 	set_reg(priv, REGPHY_TIMING, &config->p_timing);
 	set_reg(priv, REGPHY_CAL, &config->p_cal);
 
+	if (INTERACTIVE(STEP_PHY_INIT))
+		goto start;
+
 /*  4. Monitor PHY init status by polling PUBL register PGSR.IDONE
  *     Perform DDR PHY DRAM initialization and Gate Training Evaluation
  */
@@ -512,4 +807,7 @@ void stm32mp1_ddr_init(struct ddr_info *priv,
 	/* enable uMCTL2 AXI port 0 and 1 */
 	setbits_le32(&priv->ctl->pctrl_0, DDRCTRL_PCTRL_N_PORT_EN);
 	setbits_le32(&priv->ctl->pctrl_1, DDRCTRL_PCTRL_N_PORT_EN);
+
+	if (INTERACTIVE(STEP_DDR_READY))
+		goto start;
 }
diff --git a/drivers/ram/stm32mp1/stm32mp1_interactive.c b/drivers/ram/stm32mp1/stm32mp1_interactive.c
new file mode 100644
index 0000000..1a31948
--- /dev/null
+++ b/drivers/ram/stm32mp1/stm32mp1_interactive.c
@@ -0,0 +1,390 @@
+// SPDX-License-Identifier: GPL-2.0+ OR BSD-3-Clause
+/*
+ * Copyright (C) 2019, STMicroelectronics - All Rights Reserved
+ */
+
+#include <common.h>
+#include <console.h>
+#include <cli.h>
+#include <clk.h>
+#include <malloc.h>
+#include <ram.h>
+#include <reset.h>
+#include "stm32mp1_ddr.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_TUNING,
+	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",
+	};
+	/* 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 },
+	};
+	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"
+		"\nwith for [type|reg]:\n"
+		"  all registers if absent\n"
+		"  <type> = ctl, phy\n"
+		"           or one category (static, timing, map, perf, cal, 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;
+}
+
+bool stm32mp1_ddr_interactive(void *priv,
+			      enum stm32mp1_ddr_interact_step step,
+			      const struct stm32mp1_ddr_config *config)
+{
+	const char *prompt = "DDR>";
+	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() && (getc() == 'd')) {
+				next_step = STEP_DDR_RESET;
+				break;
+			}
+			if (get_timer(start) > 100)
+				break;
+		}
+#endif
+	}
+
+	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]);
+	printf("%s\n", prompt);
+
+	if (next_step > step)
+		return false;
+
+	while (next_step == step) {
+		cli_readline_into_buffer(prompt, 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;
+
+		default:
+			break;
+		}
+	}
+	return next_step == STEP_DDR_RESET;
+}
-- 
2.7.4



More information about the U-Boot mailing list