stm32mp1: ram: add interactive mode for DDR configuration

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@st.com>
This commit is contained in:
Patrick Delaunay 2019-04-10 14:09:27 +02:00 committed by Patrice Chotard
parent 1767ac2d1f
commit 01a7510849
5 changed files with 714 additions and 0 deletions

View file

@ -124,6 +124,7 @@ endif
obj-y += cli.o obj-y += cli.o
obj-$(CONFIG_FSL_DDR_INTERACTIVE) += cli_simple.o cli_readline.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-$(CONFIG_DFU_OVER_USB) += dfu.o
obj-y += command.o obj-y += command.o
obj-$(CONFIG_$(SPL_TPL_)LOG) += log.o obj-$(CONFIG_$(SPL_TPL_)LOG) += log.o

View file

@ -10,3 +10,22 @@ config STM32MP1_DDR
family: support for LPDDR2, LPDDR3 and DDR3 family: support for LPDDR2, LPDDR3 and DDR3
the SDRAM parameters for controleur and phy need to be provided the SDRAM parameters for controleur and phy need to be provided
in device tree (computed by DDR tuning tools) 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

View file

@ -5,3 +5,9 @@
obj-y += stm32mp1_ram.o obj-y += stm32mp1_ram.o
obj-y += stm32mp1_ddr.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

View file

@ -41,6 +41,16 @@ struct reg_desc {
offsetof(struct stm32mp1_ddrphy, x),\ offsetof(struct stm32mp1_ddrphy, x),\
offsetof(struct y, 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 : * PARAMETERS: value get from device tree :
* size / order need to be aligned with binding * 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), 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 * REGISTERS ARRAY: used to parse device tree and interactive mode
*****************************************************************/ *****************************************************************/
@ -190,6 +236,13 @@ enum reg_type {
REGPHY_REG, REGPHY_REG,
REGPHY_TIMING, REGPHY_TIMING,
REGPHY_CAL, 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 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}, "timing", ddrphy_timing, DDRPHY_REG_TIMING_SIZE, DDRPHY_BASE},
[REGPHY_CAL] = { [REGPHY_CAL] = {
"cal", ddrphy_cal, DDRPHY_REG_CAL_SIZE, DDRPHY_BASE}, "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[] = { 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) static void ddrphy_idone_wait(struct stm32mp1_ddrphy *phy)
{ {
u32 pgsr; u32 pgsr;
@ -394,6 +679,7 @@ void stm32mp1_ddr_init(struct ddr_info *priv,
if (ret) if (ret)
panic("ddr power init failed\n"); panic("ddr power init failed\n");
start:
debug("name = %s\n", config->info.name); debug("name = %s\n", config->info.name);
debug("speed = %d kHz\n", config->info.speed); debug("speed = %d kHz\n", config->info.speed);
debug("size = 0x%x\n", config->info.size); debug("size = 0x%x\n", config->info.size);
@ -427,6 +713,9 @@ void stm32mp1_ddr_init(struct ddr_info *priv,
udelay(2); udelay(2);
/* for PCLK = 133MHz => 1 us is enough, 2 to allow lower frequency */ /* 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 */ /* 1.5. initialize registers ddr_umctl2 */
/* Stop uMCTL2 before PHY is ready */ /* Stop uMCTL2 before PHY is ready */
clrbits_le32(&priv->ctl->dfimisc, DDRCTRL_DFIMISC_DFI_INIT_COMPLETE_EN); 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); 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 */ /* 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_DDRCORERST);
clrbits_le32(priv->rcc + RCC_DDRITFCR, RCC_DDRITFCR_DDRCAXIRST); 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_TIMING, &config->p_timing);
set_reg(priv, REGPHY_CAL, &config->p_cal); 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 /* 4. Monitor PHY init status by polling PUBL register PGSR.IDONE
* Perform DDR PHY DRAM initialization and Gate Training Evaluation * 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 */ /* enable uMCTL2 AXI port 0 and 1 */
setbits_le32(&priv->ctl->pctrl_0, DDRCTRL_PCTRL_N_PORT_EN); setbits_le32(&priv->ctl->pctrl_0, DDRCTRL_PCTRL_N_PORT_EN);
setbits_le32(&priv->ctl->pctrl_1, DDRCTRL_PCTRL_N_PORT_EN); setbits_le32(&priv->ctl->pctrl_1, DDRCTRL_PCTRL_N_PORT_EN);
if (INTERACTIVE(STEP_DDR_READY))
goto start;
} }

View file

@ -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;
}