diff --git a/common/Makefile b/common/Makefile index 8c92feb399..c7e41ef307 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 b9c816662c..511eaf2c4c 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 79eb028fab..7b1f095f90 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 cfd223e37f..d765a46f7c 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 0000000000..1a31948cb1 --- /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 +#include +#include +#include +#include +#include +#include +#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 changes DDR information\n" + " with = step, name, size or speed\n" + "freq displays the DDR PHY frequency in kHz\n" + "freq changes the DDR PHY frequency\n" + "param [type|reg] prints input parameters\n" + "param edits parameters in step 0\n" + "print [type|reg] dumps registers\n" + "edit modifies one register\n" + "step lists the available step\n" + "step go to the step \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" + " = ctl, phy\n" + " or one category (static, timing, map, perf, cal, dyn)\n" + " = 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; +}