mirror of
				https://xff.cz/git/u-boot/
				synced 2025-10-31 10:26:10 +01:00 
			
		
		
		
	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:
		
				
					committed by
					
						 Patrice Chotard
						Patrice Chotard
					
				
			
			
				
	
			
			
			
						parent
						
							1767ac2d1f
						
					
				
				
					commit
					01a7510849
				
			| @@ -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 | ||||
|   | ||||
| @@ -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 | ||||
|   | ||||
| @@ -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 | ||||
|   | ||||
| @@ -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; | ||||
| } | ||||
|   | ||||
							
								
								
									
										390
									
								
								drivers/ram/stm32mp1/stm32mp1_interactive.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										390
									
								
								drivers/ram/stm32mp1/stm32mp1_interactive.c
									
									
									
									
									
										Normal 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; | ||||
| } | ||||
		Reference in New Issue
	
	Block a user