aboutsummaryrefslogtreecommitdiffstats
path: root/roms/u-boot/drivers/ram/stm32mp1
diff options
context:
space:
mode:
Diffstat (limited to 'roms/u-boot/drivers/ram/stm32mp1')
-rw-r--r--roms/u-boot/drivers/ram/stm32mp1/Kconfig49
-rw-r--r--roms/u-boot/drivers/ram/stm32mp1/Makefile15
-rw-r--r--roms/u-boot/drivers/ram/stm32mp1/stm32mp1_ddr.c846
-rw-r--r--roms/u-boot/drivers/ram/stm32mp1/stm32mp1_ddr.h206
-rw-r--r--roms/u-boot/drivers/ram/stm32mp1/stm32mp1_ddr_regs.h369
-rw-r--r--roms/u-boot/drivers/ram/stm32mp1/stm32mp1_interactive.c497
-rw-r--r--roms/u-boot/drivers/ram/stm32mp1/stm32mp1_ram.c243
-rw-r--r--roms/u-boot/drivers/ram/stm32mp1/stm32mp1_tests.c1504
-rw-r--r--roms/u-boot/drivers/ram/stm32mp1/stm32mp1_tests.h34
-rw-r--r--roms/u-boot/drivers/ram/stm32mp1/stm32mp1_tuning.c1540
10 files changed, 5303 insertions, 0 deletions
diff --git a/roms/u-boot/drivers/ram/stm32mp1/Kconfig b/roms/u-boot/drivers/ram/stm32mp1/Kconfig
new file mode 100644
index 000000000..2fd8c7b7e
--- /dev/null
+++ b/roms/u-boot/drivers/ram/stm32mp1/Kconfig
@@ -0,0 +1,49 @@
+
+config STM32MP1_DDR
+ bool "STM32MP1 DDR driver"
+ depends on DM && OF_CONTROL && ARCH_STM32MP
+ select RAM
+ select SPL_RAM if SPL
+ default y
+ help
+ activate STM32MP1 DDR controller driver for STM32MP1 soc
+ 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
+
+config STM32MP1_DDR_TESTS
+ bool "STM32MP1 DDR driver : tests support"
+ depends on STM32MP1_DDR_INTERACTIVE
+ default y
+ help
+ activate test support for interactive support in
+ STM32MP1 DDR controller driver: command test
+
+config STM32MP1_DDR_TUNING
+ bool "STM32MP1 DDR driver : support of tuning"
+ depends on STM32MP1_DDR_INTERACTIVE
+ default y
+ help
+ activate tuning command in STM32MP1 DDR interactive mode
+ used for DDR tuning tools
+ - DQ Deskew algorithm
+ - DQS Trimming
diff --git a/roms/u-boot/drivers/ram/stm32mp1/Makefile b/roms/u-boot/drivers/ram/stm32mp1/Makefile
new file mode 100644
index 000000000..e1e913560
--- /dev/null
+++ b/roms/u-boot/drivers/ram/stm32mp1/Makefile
@@ -0,0 +1,15 @@
+# SPDX-License-Identifier: GPL-2.0+ OR BSD-3-Clause
+#
+# Copyright (C) 2018, STMicroelectronics - All Rights Reserved
+#
+
+obj-y += stm32mp1_ram.o
+obj-y += stm32mp1_ddr.o
+
+obj-$(CONFIG_STM32MP1_DDR_INTERACTIVE) += stm32mp1_interactive.o
+obj-$(CONFIG_STM32MP1_DDR_TESTS) += stm32mp1_tests.o
+obj-$(CONFIG_STM32MP1_DDR_TUNING) += stm32mp1_tuning.o
+
+ifneq ($(DDR_INTERACTIVE),)
+CFLAGS_stm32mp1_interactive.o += -DCONFIG_STM32MP1_DDR_INTERACTIVE_FORCE=y
+endif
diff --git a/roms/u-boot/drivers/ram/stm32mp1/stm32mp1_ddr.c b/roms/u-boot/drivers/ram/stm32mp1/stm32mp1_ddr.c
new file mode 100644
index 000000000..0457166b1
--- /dev/null
+++ b/roms/u-boot/drivers/ram/stm32mp1/stm32mp1_ddr.c
@@ -0,0 +1,846 @@
+// SPDX-License-Identifier: GPL-2.0+ OR BSD-3-Clause
+/*
+ * Copyright (C) 2018, STMicroelectronics - All Rights Reserved
+ */
+
+#define LOG_CATEGORY UCLASS_RAM
+
+#include <common.h>
+#include <clk.h>
+#include <log.h>
+#include <ram.h>
+#include <reset.h>
+#include <timer.h>
+#include <asm/io.h>
+#include <asm/arch/ddr.h>
+#include <linux/bitops.h>
+#include <linux/delay.h>
+#include <linux/iopoll.h>
+#include "stm32mp1_ddr.h"
+#include "stm32mp1_ddr_regs.h"
+
+#define RCC_DDRITFCR 0xD8
+
+#define RCC_DDRITFCR_DDRCAPBRST (BIT(14))
+#define RCC_DDRITFCR_DDRCAXIRST (BIT(15))
+#define RCC_DDRITFCR_DDRCORERST (BIT(16))
+#define RCC_DDRITFCR_DPHYAPBRST (BIT(17))
+#define RCC_DDRITFCR_DPHYRST (BIT(18))
+#define RCC_DDRITFCR_DPHYCTLRST (BIT(19))
+
+struct reg_desc {
+ const char *name;
+ u16 offset; /* offset for base address */
+ u8 par_offset; /* offset for parameter array */
+};
+
+#define INVALID_OFFSET 0xFF
+
+#define DDRCTL_REG(x, y) \
+ {#x,\
+ offsetof(struct stm32mp1_ddrctl, x),\
+ offsetof(struct y, x)}
+
+#define DDRPHY_REG(x, y) \
+ {#x,\
+ 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
+ * modification NOT ALLOWED !!!
+ ***********************************************************/
+#define DDRCTL_REG_REG_SIZE 25 /* st,ctl-reg */
+#define DDRCTL_REG_TIMING_SIZE 12 /* st,ctl-timing */
+#define DDRCTL_REG_MAP_SIZE 9 /* st,ctl-map */
+#define DDRCTL_REG_PERF_SIZE 17 /* st,ctl-perf */
+
+#define DDRPHY_REG_REG_SIZE 11 /* st,phy-reg */
+#define DDRPHY_REG_TIMING_SIZE 10 /* st,phy-timing */
+#define DDRPHY_REG_CAL_SIZE 12 /* st,phy-cal */
+
+#define DDRCTL_REG_REG(x) DDRCTL_REG(x, stm32mp1_ddrctrl_reg)
+static const struct reg_desc ddr_reg[DDRCTL_REG_REG_SIZE] = {
+ DDRCTL_REG_REG(mstr),
+ DDRCTL_REG_REG(mrctrl0),
+ DDRCTL_REG_REG(mrctrl1),
+ DDRCTL_REG_REG(derateen),
+ DDRCTL_REG_REG(derateint),
+ DDRCTL_REG_REG(pwrctl),
+ DDRCTL_REG_REG(pwrtmg),
+ DDRCTL_REG_REG(hwlpctl),
+ DDRCTL_REG_REG(rfshctl0),
+ DDRCTL_REG_REG(rfshctl3),
+ DDRCTL_REG_REG(crcparctl0),
+ DDRCTL_REG_REG(zqctl0),
+ DDRCTL_REG_REG(dfitmg0),
+ DDRCTL_REG_REG(dfitmg1),
+ DDRCTL_REG_REG(dfilpcfg0),
+ DDRCTL_REG_REG(dfiupd0),
+ DDRCTL_REG_REG(dfiupd1),
+ DDRCTL_REG_REG(dfiupd2),
+ DDRCTL_REG_REG(dfiphymstr),
+ DDRCTL_REG_REG(odtmap),
+ DDRCTL_REG_REG(dbg0),
+ DDRCTL_REG_REG(dbg1),
+ DDRCTL_REG_REG(dbgcmd),
+ DDRCTL_REG_REG(poisoncfg),
+ DDRCTL_REG_REG(pccfg),
+};
+
+#define DDRCTL_REG_TIMING(x) DDRCTL_REG(x, stm32mp1_ddrctrl_timing)
+static const struct reg_desc ddr_timing[DDRCTL_REG_TIMING_SIZE] = {
+ DDRCTL_REG_TIMING(rfshtmg),
+ DDRCTL_REG_TIMING(dramtmg0),
+ DDRCTL_REG_TIMING(dramtmg1),
+ DDRCTL_REG_TIMING(dramtmg2),
+ DDRCTL_REG_TIMING(dramtmg3),
+ DDRCTL_REG_TIMING(dramtmg4),
+ DDRCTL_REG_TIMING(dramtmg5),
+ DDRCTL_REG_TIMING(dramtmg6),
+ DDRCTL_REG_TIMING(dramtmg7),
+ DDRCTL_REG_TIMING(dramtmg8),
+ DDRCTL_REG_TIMING(dramtmg14),
+ DDRCTL_REG_TIMING(odtcfg),
+};
+
+#define DDRCTL_REG_MAP(x) DDRCTL_REG(x, stm32mp1_ddrctrl_map)
+static const struct reg_desc ddr_map[DDRCTL_REG_MAP_SIZE] = {
+ DDRCTL_REG_MAP(addrmap1),
+ DDRCTL_REG_MAP(addrmap2),
+ DDRCTL_REG_MAP(addrmap3),
+ DDRCTL_REG_MAP(addrmap4),
+ DDRCTL_REG_MAP(addrmap5),
+ DDRCTL_REG_MAP(addrmap6),
+ DDRCTL_REG_MAP(addrmap9),
+ DDRCTL_REG_MAP(addrmap10),
+ DDRCTL_REG_MAP(addrmap11),
+};
+
+#define DDRCTL_REG_PERF(x) DDRCTL_REG(x, stm32mp1_ddrctrl_perf)
+static const struct reg_desc ddr_perf[DDRCTL_REG_PERF_SIZE] = {
+ DDRCTL_REG_PERF(sched),
+ DDRCTL_REG_PERF(sched1),
+ DDRCTL_REG_PERF(perfhpr1),
+ DDRCTL_REG_PERF(perflpr1),
+ DDRCTL_REG_PERF(perfwr1),
+ DDRCTL_REG_PERF(pcfgr_0),
+ DDRCTL_REG_PERF(pcfgw_0),
+ DDRCTL_REG_PERF(pcfgqos0_0),
+ DDRCTL_REG_PERF(pcfgqos1_0),
+ DDRCTL_REG_PERF(pcfgwqos0_0),
+ DDRCTL_REG_PERF(pcfgwqos1_0),
+ DDRCTL_REG_PERF(pcfgr_1),
+ DDRCTL_REG_PERF(pcfgw_1),
+ DDRCTL_REG_PERF(pcfgqos0_1),
+ DDRCTL_REG_PERF(pcfgqos1_1),
+ DDRCTL_REG_PERF(pcfgwqos0_1),
+ DDRCTL_REG_PERF(pcfgwqos1_1),
+};
+
+#define DDRPHY_REG_REG(x) DDRPHY_REG(x, stm32mp1_ddrphy_reg)
+static const struct reg_desc ddrphy_reg[DDRPHY_REG_REG_SIZE] = {
+ DDRPHY_REG_REG(pgcr),
+ DDRPHY_REG_REG(aciocr),
+ DDRPHY_REG_REG(dxccr),
+ DDRPHY_REG_REG(dsgcr),
+ DDRPHY_REG_REG(dcr),
+ DDRPHY_REG_REG(odtcr),
+ DDRPHY_REG_REG(zq0cr1),
+ DDRPHY_REG_REG(dx0gcr),
+ DDRPHY_REG_REG(dx1gcr),
+ DDRPHY_REG_REG(dx2gcr),
+ DDRPHY_REG_REG(dx3gcr),
+};
+
+#define DDRPHY_REG_TIMING(x) DDRPHY_REG(x, stm32mp1_ddrphy_timing)
+static const struct reg_desc ddrphy_timing[DDRPHY_REG_TIMING_SIZE] = {
+ DDRPHY_REG_TIMING(ptr0),
+ DDRPHY_REG_TIMING(ptr1),
+ DDRPHY_REG_TIMING(ptr2),
+ DDRPHY_REG_TIMING(dtpr0),
+ DDRPHY_REG_TIMING(dtpr1),
+ DDRPHY_REG_TIMING(dtpr2),
+ DDRPHY_REG_TIMING(mr0),
+ DDRPHY_REG_TIMING(mr1),
+ DDRPHY_REG_TIMING(mr2),
+ DDRPHY_REG_TIMING(mr3),
+};
+
+#define DDRPHY_REG_CAL(x) DDRPHY_REG(x, stm32mp1_ddrphy_cal)
+static const struct reg_desc ddrphy_cal[DDRPHY_REG_CAL_SIZE] = {
+ DDRPHY_REG_CAL(dx0dllcr),
+ DDRPHY_REG_CAL(dx0dqtr),
+ DDRPHY_REG_CAL(dx0dqstr),
+ DDRPHY_REG_CAL(dx1dllcr),
+ DDRPHY_REG_CAL(dx1dqtr),
+ DDRPHY_REG_CAL(dx1dqstr),
+ DDRPHY_REG_CAL(dx2dllcr),
+ DDRPHY_REG_CAL(dx2dqtr),
+ DDRPHY_REG_CAL(dx2dqstr),
+ DDRPHY_REG_CAL(dx3dllcr),
+ DDRPHY_REG_CAL(dx3dqtr),
+ 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
+ *****************************************************************/
+enum reg_type {
+ REG_REG,
+ REG_TIMING,
+ REG_PERF,
+ REG_MAP,
+ 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
+};
+
+enum base_type {
+ DDR_BASE,
+ DDRPHY_BASE,
+ NONE_BASE
+};
+
+struct ddr_reg_info {
+ const char *name;
+ const struct reg_desc *desc;
+ u8 size;
+ enum base_type base;
+};
+
+#define DDRPHY_REG_CAL(x) DDRPHY_REG(x, stm32mp1_ddrphy_cal)
+
+const struct ddr_reg_info ddr_registers[REG_TYPE_NB] = {
+[REG_REG] = {
+ "static", ddr_reg, DDRCTL_REG_REG_SIZE, DDR_BASE},
+[REG_TIMING] = {
+ "timing", ddr_timing, DDRCTL_REG_TIMING_SIZE, DDR_BASE},
+[REG_PERF] = {
+ "perf", ddr_perf, DDRCTL_REG_PERF_SIZE, DDR_BASE},
+[REG_MAP] = {
+ "map", ddr_map, DDRCTL_REG_MAP_SIZE, DDR_BASE},
+[REGPHY_REG] = {
+ "static", ddrphy_reg, DDRPHY_REG_REG_SIZE, DDRPHY_BASE},
+[REGPHY_TIMING] = {
+ "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[] = {
+ [DDR_BASE] = "ctl",
+ [DDRPHY_BASE] = "phy",
+};
+
+static u32 get_base_addr(const struct ddr_info *priv, enum base_type base)
+{
+ if (base == DDRPHY_BASE)
+ return (u32)priv->phy;
+ else
+ return (u32)priv->ctl;
+}
+
+static void set_reg(const struct ddr_info *priv,
+ enum reg_type type,
+ const void *param)
+{
+ unsigned int i;
+ unsigned int *ptr, value;
+ enum base_type base = ddr_registers[type].base;
+ u32 base_addr = get_base_addr(priv, base);
+ const struct reg_desc *desc = ddr_registers[type].desc;
+
+ log_debug("init %s\n", ddr_registers[type].name);
+ for (i = 0; i < ddr_registers[type].size; i++) {
+ ptr = (unsigned int *)(base_addr + desc[i].offset);
+ if (desc[i].par_offset == INVALID_OFFSET) {
+ log_err("invalid parameter offset for %s", desc[i].name);
+ } else {
+ value = *((u32 *)((u32)param +
+ desc[i].par_offset));
+ writel(value, ptr);
+ log_debug("[0x%x] %s= 0x%08x\n",
+ (u32)ptr, desc[i].name, value);
+ }
+ }
+}
+
+#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;
+ int ret;
+
+ ret = readl_poll_timeout(&phy->pgsr, pgsr,
+ pgsr & (DDRPHYC_PGSR_IDONE |
+ DDRPHYC_PGSR_DTERR |
+ DDRPHYC_PGSR_DTIERR |
+ DDRPHYC_PGSR_DFTERR |
+ DDRPHYC_PGSR_RVERR |
+ DDRPHYC_PGSR_RVEIRR),
+ 1000000);
+ log_debug("\n[0x%08x] pgsr = 0x%08x ret=%d\n",
+ (u32)&phy->pgsr, pgsr, ret);
+}
+
+void stm32mp1_ddrphy_init(struct stm32mp1_ddrphy *phy, u32 pir)
+{
+ pir |= DDRPHYC_PIR_INIT;
+ writel(pir, &phy->pir);
+ log_debug("[0x%08x] pir = 0x%08x -> 0x%08x\n",
+ (u32)&phy->pir, pir, readl(&phy->pir));
+
+ /* need to wait 10 configuration clock before start polling */
+ udelay(10);
+
+ /* Wait DRAM initialization and Gate Training Evaluation complete */
+ ddrphy_idone_wait(phy);
+}
+
+/* start quasi dynamic register update */
+static void start_sw_done(struct stm32mp1_ddrctl *ctl)
+{
+ clrbits_le32(&ctl->swctl, DDRCTRL_SWCTL_SW_DONE);
+}
+
+/* wait quasi dynamic register update */
+static void wait_sw_done_ack(struct stm32mp1_ddrctl *ctl)
+{
+ int ret;
+ u32 swstat;
+
+ setbits_le32(&ctl->swctl, DDRCTRL_SWCTL_SW_DONE);
+
+ ret = readl_poll_timeout(&ctl->swstat, swstat,
+ swstat & DDRCTRL_SWSTAT_SW_DONE_ACK,
+ 1000000);
+ if (ret)
+ panic("Timeout initialising DRAM : DDR->swstat = %x\n",
+ swstat);
+
+ log_debug("[0x%08x] swstat = 0x%08x\n", (u32)&ctl->swstat, swstat);
+}
+
+/* wait quasi dynamic register update */
+static void wait_operating_mode(struct ddr_info *priv, int mode)
+{
+ u32 stat, val, mask, val2 = 0, mask2 = 0;
+ int ret;
+
+ mask = DDRCTRL_STAT_OPERATING_MODE_MASK;
+ val = mode;
+ /* self-refresh due to software => check also STAT.selfref_type */
+ if (mode == DDRCTRL_STAT_OPERATING_MODE_SR) {
+ mask |= DDRCTRL_STAT_SELFREF_TYPE_MASK;
+ val |= DDRCTRL_STAT_SELFREF_TYPE_SR;
+ } else if (mode == DDRCTRL_STAT_OPERATING_MODE_NORMAL) {
+ /* normal mode: handle also automatic self refresh */
+ mask2 = DDRCTRL_STAT_OPERATING_MODE_MASK |
+ DDRCTRL_STAT_SELFREF_TYPE_MASK;
+ val2 = DDRCTRL_STAT_OPERATING_MODE_SR |
+ DDRCTRL_STAT_SELFREF_TYPE_ASR;
+ }
+
+ ret = readl_poll_timeout(&priv->ctl->stat, stat,
+ ((stat & mask) == val) ||
+ (mask2 && ((stat & mask2) == val2)),
+ 1000000);
+
+ if (ret)
+ panic("Timeout DRAM : DDR->stat = %x\n", stat);
+
+ log_debug("[0x%08x] stat = 0x%08x\n", (u32)&priv->ctl->stat, stat);
+}
+
+void stm32mp1_refresh_disable(struct stm32mp1_ddrctl *ctl)
+{
+ start_sw_done(ctl);
+ /* quasi-dynamic register update*/
+ setbits_le32(&ctl->rfshctl3, DDRCTRL_RFSHCTL3_DIS_AUTO_REFRESH);
+ clrbits_le32(&ctl->pwrctl, DDRCTRL_PWRCTL_POWERDOWN_EN |
+ DDRCTRL_PWRCTL_SELFREF_EN);
+ clrbits_le32(&ctl->dfimisc, DDRCTRL_DFIMISC_DFI_INIT_COMPLETE_EN);
+ wait_sw_done_ack(ctl);
+}
+
+void stm32mp1_refresh_restore(struct stm32mp1_ddrctl *ctl,
+ u32 rfshctl3, u32 pwrctl)
+{
+ start_sw_done(ctl);
+ if (!(rfshctl3 & DDRCTRL_RFSHCTL3_DIS_AUTO_REFRESH))
+ clrbits_le32(&ctl->rfshctl3, DDRCTRL_RFSHCTL3_DIS_AUTO_REFRESH);
+ if (pwrctl & DDRCTRL_PWRCTL_POWERDOWN_EN)
+ setbits_le32(&ctl->pwrctl, DDRCTRL_PWRCTL_POWERDOWN_EN);
+ if ((pwrctl & DDRCTRL_PWRCTL_SELFREF_EN))
+ setbits_le32(&ctl->pwrctl, DDRCTRL_PWRCTL_SELFREF_EN);
+ setbits_le32(&ctl->dfimisc, DDRCTRL_DFIMISC_DFI_INIT_COMPLETE_EN);
+ wait_sw_done_ack(ctl);
+}
+
+/* board-specific DDR power initializations. */
+__weak int board_ddr_power_init(enum ddr_type ddr_type)
+{
+ return 0;
+}
+
+__maybe_unused
+void stm32mp1_ddr_init(struct ddr_info *priv,
+ const struct stm32mp1_ddr_config *config)
+{
+ u32 pir;
+ int ret = -EINVAL;
+ char bus_width;
+
+ switch (config->c_reg.mstr & DDRCTRL_MSTR_DATA_BUS_WIDTH_MASK) {
+ case DDRCTRL_MSTR_DATA_BUS_WIDTH_QUARTER:
+ bus_width = 8;
+ break;
+ case DDRCTRL_MSTR_DATA_BUS_WIDTH_HALF:
+ bus_width = 16;
+ break;
+ default:
+ bus_width = 32;
+ break;
+ }
+
+
+ if (config->c_reg.mstr & DDRCTRL_MSTR_DDR3)
+ ret = board_ddr_power_init(STM32MP_DDR3);
+ else if (config->c_reg.mstr & DDRCTRL_MSTR_LPDDR2) {
+ if (bus_width == 32)
+ ret = board_ddr_power_init(STM32MP_LPDDR2_32);
+ else
+ ret = board_ddr_power_init(STM32MP_LPDDR2_16);
+ } else if (config->c_reg.mstr & DDRCTRL_MSTR_LPDDR3) {
+ if (bus_width == 32)
+ ret = board_ddr_power_init(STM32MP_LPDDR3_32);
+ else
+ ret = board_ddr_power_init(STM32MP_LPDDR3_16);
+ }
+ if (ret)
+ panic("ddr power init failed\n");
+
+start:
+ log_debug("name = %s\n", config->info.name);
+ log_debug("speed = %d kHz\n", config->info.speed);
+ log_debug("size = 0x%x\n", config->info.size);
+/*
+ * 1. Program the DWC_ddr_umctl2 registers
+ * 1.1 RESETS: presetn, core_ddrc_rstn, aresetn
+ */
+ /* Assert All DDR part */
+ setbits_le32(priv->rcc + RCC_DDRITFCR, RCC_DDRITFCR_DDRCAPBRST);
+ setbits_le32(priv->rcc + RCC_DDRITFCR, RCC_DDRITFCR_DDRCAXIRST);
+ setbits_le32(priv->rcc + RCC_DDRITFCR, RCC_DDRITFCR_DDRCORERST);
+ setbits_le32(priv->rcc + RCC_DDRITFCR, RCC_DDRITFCR_DPHYAPBRST);
+ setbits_le32(priv->rcc + RCC_DDRITFCR, RCC_DDRITFCR_DPHYRST);
+ setbits_le32(priv->rcc + RCC_DDRITFCR, RCC_DDRITFCR_DPHYCTLRST);
+
+/* 1.2. start CLOCK */
+ if (stm32mp1_ddr_clk_enable(priv, config->info.speed))
+ panic("invalid DRAM clock : %d kHz\n",
+ config->info.speed);
+
+/* 1.3. deassert reset */
+ /* de-assert PHY rstn and ctl_rstn via DPHYRST and DPHYCTLRST */
+ clrbits_le32(priv->rcc + RCC_DDRITFCR, RCC_DDRITFCR_DPHYRST);
+ clrbits_le32(priv->rcc + RCC_DDRITFCR, RCC_DDRITFCR_DPHYCTLRST);
+ /* De-assert presetn once the clocks are active
+ * and stable via DDRCAPBRST bit
+ */
+ clrbits_le32(priv->rcc + RCC_DDRITFCR, RCC_DDRITFCR_DDRCAPBRST);
+
+/* 1.4. wait 128 cycles to permit initialization of end logic */
+ 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);
+ log_debug("[0x%08x] dfimisc = 0x%08x\n",
+ (u32)&priv->ctl->dfimisc, readl(&priv->ctl->dfimisc));
+
+ set_reg(priv, REG_REG, &config->c_reg);
+ set_reg(priv, REG_TIMING, &config->c_timing);
+ set_reg(priv, REG_MAP, &config->c_map);
+
+ /* skip CTRL init, SDRAM init is done by PHY PUBL */
+ clrsetbits_le32(&priv->ctl->init0,
+ DDRCTRL_INIT0_SKIP_DRAM_INIT_MASK,
+ DDRCTRL_INIT0_SKIP_DRAM_INIT_NORMAL);
+
+ 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);
+ clrbits_le32(priv->rcc + RCC_DDRITFCR, RCC_DDRITFCR_DPHYAPBRST);
+
+/* 3. start PHY init by accessing relevant PUBL registers
+ * (DXGCR, DCR, PTR*, MR*, DTPR*)
+ */
+ set_reg(priv, REGPHY_REG, &config->p_reg);
+ set_reg(priv, REGPHY_TIMING, &config->p_timing);
+ if (config->p_cal_present)
+ 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
+ */
+ ddrphy_idone_wait(priv->phy);
+
+/* 5. Indicate to PUBL that controller performs SDRAM initialization
+ * by setting PIR.INIT and PIR CTLDINIT and pool PGSR.IDONE
+ * DRAM init is done by PHY, init0.skip_dram.init = 1
+ */
+ pir = DDRPHYC_PIR_DLLSRST | DDRPHYC_PIR_DLLLOCK | DDRPHYC_PIR_ZCAL |
+ DDRPHYC_PIR_ITMSRST | DDRPHYC_PIR_DRAMINIT | DDRPHYC_PIR_ICPC;
+
+ if (config->c_reg.mstr & DDRCTRL_MSTR_DDR3)
+ pir |= DDRPHYC_PIR_DRAMRST; /* only for DDR3 */
+
+ stm32mp1_ddrphy_init(priv->phy, pir);
+
+/* 6. SET DFIMISC.dfi_init_complete_en to 1 */
+ /* Enable quasi-dynamic register programming*/
+ start_sw_done(priv->ctl);
+ setbits_le32(&priv->ctl->dfimisc, DDRCTRL_DFIMISC_DFI_INIT_COMPLETE_EN);
+ wait_sw_done_ack(priv->ctl);
+
+/* 7. Wait for DWC_ddr_umctl2 to move to normal operation mode
+ * by monitoring STAT.operating_mode signal
+ */
+ /* wait uMCTL2 ready */
+
+ wait_operating_mode(priv, DDRCTRL_STAT_OPERATING_MODE_NORMAL);
+
+ if (config->p_cal_present) {
+ log_debug("DDR DQS training skipped.\n");
+ } else {
+ log_debug("DDR DQS training : ");
+/* 8. Disable Auto refresh and power down by setting
+ * - RFSHCTL3.dis_au_refresh = 1
+ * - PWRCTL.powerdown_en = 0
+ * - DFIMISC.dfiinit_complete_en = 0
+ */
+ stm32mp1_refresh_disable(priv->ctl);
+
+/* 9. Program PUBL PGCR to enable refresh during training and rank to train
+ * not done => keep the programed value in PGCR
+ */
+
+/* 10. configure PUBL PIR register to specify which training step to run */
+ /* warning : RVTRN is not supported by this PUBL */
+ stm32mp1_ddrphy_init(priv->phy, DDRPHYC_PIR_QSTRN);
+
+/* 11. monitor PUB PGSR.IDONE to poll cpmpletion of training sequence */
+ ddrphy_idone_wait(priv->phy);
+
+/* 12. set back registers in step 8 to the orginal values if desidered */
+ stm32mp1_refresh_restore(priv->ctl, config->c_reg.rfshctl3,
+ config->c_reg.pwrctl);
+ } /* if (config->p_cal_present) */
+
+ /* 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/roms/u-boot/drivers/ram/stm32mp1/stm32mp1_ddr.h b/roms/u-boot/drivers/ram/stm32mp1/stm32mp1_ddr.h
new file mode 100644
index 000000000..4998f0443
--- /dev/null
+++ b/roms/u-boot/drivers/ram/stm32mp1/stm32mp1_ddr.h
@@ -0,0 +1,206 @@
+/* SPDX-License-Identifier: GPL-2.0+ OR BSD-3-Clause */
+/*
+ * Copyright (C) 2018, STMicroelectronics - All Rights Reserved
+ */
+
+#ifndef _RAM_STM32MP1_DDR_H
+#define _RAM_STM32MP1_DDR_H
+
+enum stm32mp1_ddr_interact_step {
+ STEP_DDR_RESET,
+ STEP_CTL_INIT,
+ STEP_PHY_INIT,
+ STEP_DDR_READY,
+ STEP_RUN,
+};
+
+/* DDR CTL and DDR PHY REGISTERS */
+struct stm32mp1_ddrctl;
+struct stm32mp1_ddrphy;
+
+/**
+ * struct ddr_info
+ *
+ * @dev: pointer for the device
+ * @info: UCLASS RAM information
+ * @ctl: DDR controleur base address
+ * @clk: DDR clock
+ * @phy: DDR PHY base address
+ * @rcc: rcc base address
+ */
+struct ddr_info {
+ struct udevice *dev;
+ struct ram_info info;
+ struct clk clk;
+ struct stm32mp1_ddrctl *ctl;
+ struct stm32mp1_ddrphy *phy;
+ u32 rcc;
+};
+
+struct stm32mp1_ddrctrl_reg {
+ u32 mstr;
+ u32 mrctrl0;
+ u32 mrctrl1;
+ u32 derateen;
+ u32 derateint;
+ u32 pwrctl;
+ u32 pwrtmg;
+ u32 hwlpctl;
+ u32 rfshctl0;
+ u32 rfshctl3;
+ u32 crcparctl0;
+ u32 zqctl0;
+ u32 dfitmg0;
+ u32 dfitmg1;
+ u32 dfilpcfg0;
+ u32 dfiupd0;
+ u32 dfiupd1;
+ u32 dfiupd2;
+ u32 dfiphymstr;
+ u32 odtmap;
+ u32 dbg0;
+ u32 dbg1;
+ u32 dbgcmd;
+ u32 poisoncfg;
+ u32 pccfg;
+
+};
+
+struct stm32mp1_ddrctrl_timing {
+ u32 rfshtmg;
+ u32 dramtmg0;
+ u32 dramtmg1;
+ u32 dramtmg2;
+ u32 dramtmg3;
+ u32 dramtmg4;
+ u32 dramtmg5;
+ u32 dramtmg6;
+ u32 dramtmg7;
+ u32 dramtmg8;
+ u32 dramtmg14;
+ u32 odtcfg;
+};
+
+struct stm32mp1_ddrctrl_map {
+ u32 addrmap1;
+ u32 addrmap2;
+ u32 addrmap3;
+ u32 addrmap4;
+ u32 addrmap5;
+ u32 addrmap6;
+ u32 addrmap9;
+ u32 addrmap10;
+ u32 addrmap11;
+};
+
+struct stm32mp1_ddrctrl_perf {
+ u32 sched;
+ u32 sched1;
+ u32 perfhpr1;
+ u32 perflpr1;
+ u32 perfwr1;
+ u32 pcfgr_0;
+ u32 pcfgw_0;
+ u32 pcfgqos0_0;
+ u32 pcfgqos1_0;
+ u32 pcfgwqos0_0;
+ u32 pcfgwqos1_0;
+ u32 pcfgr_1;
+ u32 pcfgw_1;
+ u32 pcfgqos0_1;
+ u32 pcfgqos1_1;
+ u32 pcfgwqos0_1;
+ u32 pcfgwqos1_1;
+};
+
+struct stm32mp1_ddrphy_reg {
+ u32 pgcr;
+ u32 aciocr;
+ u32 dxccr;
+ u32 dsgcr;
+ u32 dcr;
+ u32 odtcr;
+ u32 zq0cr1;
+ u32 dx0gcr;
+ u32 dx1gcr;
+ u32 dx2gcr;
+ u32 dx3gcr;
+};
+
+struct stm32mp1_ddrphy_timing {
+ u32 ptr0;
+ u32 ptr1;
+ u32 ptr2;
+ u32 dtpr0;
+ u32 dtpr1;
+ u32 dtpr2;
+ u32 mr0;
+ u32 mr1;
+ u32 mr2;
+ u32 mr3;
+};
+
+struct stm32mp1_ddrphy_cal {
+ u32 dx0dllcr;
+ u32 dx0dqtr;
+ u32 dx0dqstr;
+ u32 dx1dllcr;
+ u32 dx1dqtr;
+ u32 dx1dqstr;
+ u32 dx2dllcr;
+ u32 dx2dqtr;
+ u32 dx2dqstr;
+ u32 dx3dllcr;
+ u32 dx3dqtr;
+ u32 dx3dqstr;
+};
+
+struct stm32mp1_ddr_info {
+ const char *name;
+ u32 speed; /* in kHZ */
+ u32 size; /* memory size in byte = col * row * width */
+};
+
+struct stm32mp1_ddr_config {
+ struct stm32mp1_ddr_info info;
+ struct stm32mp1_ddrctrl_reg c_reg;
+ struct stm32mp1_ddrctrl_timing c_timing;
+ struct stm32mp1_ddrctrl_map c_map;
+ struct stm32mp1_ddrctrl_perf c_perf;
+ struct stm32mp1_ddrphy_reg p_reg;
+ struct stm32mp1_ddrphy_timing p_timing;
+ struct stm32mp1_ddrphy_cal p_cal;
+ bool p_cal_present;
+};
+
+int stm32mp1_ddr_clk_enable(struct ddr_info *priv, u32 mem_speed);
+void stm32mp1_ddrphy_init(struct stm32mp1_ddrphy *phy, u32 pir);
+void stm32mp1_refresh_disable(struct stm32mp1_ddrctl *ctl);
+void stm32mp1_refresh_restore(struct stm32mp1_ddrctl *ctl,
+ u32 rfshctl3,
+ u32 pwrctl);
+
+void stm32mp1_ddr_init(
+ struct ddr_info *priv,
+ const struct stm32mp1_ddr_config *config);
+
+int stm32mp1_dump_reg(const struct ddr_info *priv,
+ const char *name);
+
+void stm32mp1_edit_reg(const struct ddr_info *priv,
+ char *name,
+ char *string);
+
+int stm32mp1_dump_param(const struct stm32mp1_ddr_config *config,
+ const char *name);
+
+void stm32mp1_edit_param(const struct stm32mp1_ddr_config *config,
+ char *name,
+ char *string);
+
+bool stm32mp1_ddr_interactive(
+ void *priv,
+ enum stm32mp1_ddr_interact_step step,
+ const struct stm32mp1_ddr_config *config);
+
+#endif
diff --git a/roms/u-boot/drivers/ram/stm32mp1/stm32mp1_ddr_regs.h b/roms/u-boot/drivers/ram/stm32mp1/stm32mp1_ddr_regs.h
new file mode 100644
index 000000000..3c8885a96
--- /dev/null
+++ b/roms/u-boot/drivers/ram/stm32mp1/stm32mp1_ddr_regs.h
@@ -0,0 +1,369 @@
+/* SPDX-License-Identifier: GPL-2.0+ OR BSD-3-Clause */
+/*
+ * Copyright (C) 2018, STMicroelectronics - All Rights Reserved
+ */
+
+#ifndef _RAM_STM32MP1_DDR_REGS_H
+#define _RAM_STM32MP1_DDR_REGS_H
+
+/* DDR3/LPDDR2/LPDDR3 Controller (DDRCTRL) registers */
+#include <linux/bitops.h>
+struct stm32mp1_ddrctl {
+ u32 mstr ; /* 0x0 Master*/
+ u32 stat; /* 0x4 Operating Mode Status*/
+ u8 reserved008[0x10 - 0x8];
+ u32 mrctrl0; /* 0x10 Control 0.*/
+ u32 mrctrl1; /* 0x14 Control 1*/
+ u32 mrstat; /* 0x18 Status*/
+ u32 reserved01c; /* 0x1c */
+ u32 derateen; /* 0x20 Temperature Derate Enable*/
+ u32 derateint; /* 0x24 Temperature Derate Interval*/
+ u8 reserved028[0x30 - 0x28];
+ u32 pwrctl; /* 0x30 Low Power Control*/
+ u32 pwrtmg; /* 0x34 Low Power Timing*/
+ u32 hwlpctl; /* 0x38 Hardware Low Power Control*/
+ u8 reserved03c[0x50 - 0x3C];
+ u32 rfshctl0; /* 0x50 Refresh Control 0*/
+ u32 reserved054; /* 0x54 Refresh Control 1*/
+ u32 reserved058; /* 0x58 Refresh Control 2*/
+ u32 reserved05C;
+ u32 rfshctl3; /* 0x60 Refresh Control 0*/
+ u32 rfshtmg; /* 0x64 Refresh Timing*/
+ u8 reserved068[0xc0 - 0x68];
+ u32 crcparctl0; /* 0xc0 CRC Parity Control0*/
+ u32 reserved0c4; /* 0xc4 CRC Parity Control1*/
+ u32 reserved0c8; /* 0xc8 CRC Parity Control2*/
+ u32 crcparstat; /* 0xcc CRC Parity Status*/
+ u32 init0; /* 0xd0 SDRAM Initialization 0*/
+ u32 init1; /* 0xd4 SDRAM Initialization 1*/
+ u32 init2; /* 0xd8 SDRAM Initialization 2*/
+ u32 init3; /* 0xdc SDRAM Initialization 3*/
+ u32 init4; /* 0xe0 SDRAM Initialization 4*/
+ u32 init5; /* 0xe4 SDRAM Initialization 5*/
+ u32 reserved0e8;
+ u32 reserved0ec;
+ u32 dimmctl; /* 0xf0 DIMM Control*/
+ u8 reserved0f4[0x100 - 0xf4];
+ u32 dramtmg0; /* 0x100 SDRAM Timing 0*/
+ u32 dramtmg1; /* 0x104 SDRAM Timing 1*/
+ u32 dramtmg2; /* 0x108 SDRAM Timing 2*/
+ u32 dramtmg3; /* 0x10c SDRAM Timing 3*/
+ u32 dramtmg4; /* 0x110 SDRAM Timing 4*/
+ u32 dramtmg5; /* 0x114 SDRAM Timing 5*/
+ u32 dramtmg6; /* 0x118 SDRAM Timing 6*/
+ u32 dramtmg7; /* 0x11c SDRAM Timing 7*/
+ u32 dramtmg8; /* 0x120 SDRAM Timing 8*/
+ u8 reserved124[0x138 - 0x124];
+ u32 dramtmg14; /* 0x138 SDRAM Timing 14*/
+ u32 dramtmg15; /* 0x13C SDRAM Timing 15*/
+ u8 reserved140[0x180 - 0x140];
+ u32 zqctl0; /* 0x180 ZQ Control 0*/
+ u32 zqctl1; /* 0x184 ZQ Control 1*/
+ u32 zqctl2; /* 0x188 ZQ Control 2*/
+ u32 zqstat; /* 0x18c ZQ Status*/
+ u32 dfitmg0; /* 0x190 DFI Timing 0*/
+ u32 dfitmg1; /* 0x194 DFI Timing 1*/
+ u32 dfilpcfg0; /* 0x198 DFI Low Power Configuration 0*/
+ u32 reserved19c;
+ u32 dfiupd0; /* 0x1a0 DFI Update 0*/
+ u32 dfiupd1; /* 0x1a4 DFI Update 1*/
+ u32 dfiupd2; /* 0x1a8 DFI Update 2*/
+ u32 reserved1ac;
+ u32 dfimisc; /* 0x1b0 DFI Miscellaneous Control*/
+ u8 reserved1b4[0x1bc - 0x1b4];
+ u32 dfistat; /* 0x1bc DFI Miscellaneous Control*/
+ u8 reserved1c0[0x1c4 - 0x1c0];
+ u32 dfiphymstr; /* 0x1c4 DFI PHY Master interface*/
+ u8 reserved1c8[0x204 - 0x1c8];
+ u32 addrmap1; /* 0x204 Address Map 1*/
+ u32 addrmap2; /* 0x208 Address Map 2*/
+ u32 addrmap3; /* 0x20c Address Map 3*/
+ u32 addrmap4; /* 0x210 Address Map 4*/
+ u32 addrmap5; /* 0x214 Address Map 5*/
+ u32 addrmap6; /* 0x218 Address Map 6*/
+ u8 reserved21c[0x224 - 0x21c];
+ u32 addrmap9; /* 0x224 Address Map 9*/
+ u32 addrmap10; /* 0x228 Address Map 10*/
+ u32 addrmap11; /* 0x22C Address Map 11*/
+ u8 reserved230[0x240 - 0x230];
+ u32 odtcfg; /* 0x240 ODT Configuration*/
+ u32 odtmap; /* 0x244 ODT/Rank Map*/
+ u8 reserved248[0x250 - 0x248];
+ u32 sched; /* 0x250 Scheduler Control*/
+ u32 sched1; /* 0x254 Scheduler Control 1*/
+ u32 reserved258;
+ u32 perfhpr1; /* 0x25c High Priority Read CAM 1*/
+ u32 reserved260;
+ u32 perflpr1; /* 0x264 Low Priority Read CAM 1*/
+ u32 reserved268;
+ u32 perfwr1; /* 0x26c Write CAM 1*/
+ u8 reserved27c[0x300 - 0x270];
+ u32 dbg0; /* 0x300 Debug 0*/
+ u32 dbg1; /* 0x304 Debug 1*/
+ u32 dbgcam; /* 0x308 CAM Debug*/
+ u32 dbgcmd; /* 0x30c Command Debug*/
+ u32 dbgstat; /* 0x310 Status Debug*/
+ u8 reserved314[0x320 - 0x314];
+ u32 swctl; /* 0x320 Software Programming Control Enable*/
+ u32 swstat; /* 0x324 Software Programming Control Status*/
+ u8 reserved328[0x36c - 0x328];
+ u32 poisoncfg; /* 0x36c AXI Poison Configuration Register*/
+ u32 poisonstat; /* 0x370 AXI Poison Status Register*/
+ u8 reserved374[0x3fc - 0x374];
+
+ /* Multi Port registers */
+ u32 pstat; /* 0x3fc Port Status*/
+ u32 pccfg; /* 0x400 Port Common Configuration*/
+
+ /* PORT 0 */
+ u32 pcfgr_0; /* 0x404 Configuration Read*/
+ u32 pcfgw_0; /* 0x408 Configuration Write*/
+ u8 reserved40c[0x490 - 0x40c];
+ u32 pctrl_0; /* 0x490 Port Control Register */
+ u32 pcfgqos0_0; /* 0x494 Read QoS Configuration 0*/
+ u32 pcfgqos1_0; /* 0x498 Read QoS Configuration 1*/
+ u32 pcfgwqos0_0; /* 0x49c Write QoS Configuration 0*/
+ u32 pcfgwqos1_0; /* 0x4a0 Write QoS Configuration 1*/
+ u8 reserved4a4[0x4b4 - 0x4a4];
+
+ /* PORT 1 */
+ u32 pcfgr_1; /* 0x4b4 Configuration Read*/
+ u32 pcfgw_1; /* 0x4b8 Configuration Write*/
+ u8 reserved4bc[0x540 - 0x4bc];
+ u32 pctrl_1; /* 0x540 Port 2 Control Register */
+ u32 pcfgqos0_1; /* 0x544 Read QoS Configuration 0*/
+ u32 pcfgqos1_1; /* 0x548 Read QoS Configuration 1*/
+ u32 pcfgwqos0_1; /* 0x54c Write QoS Configuration 0*/
+ u32 pcfgwqos1_1; /* 0x550 Write QoS Configuration 1*/
+};
+
+/* DDR Physical Interface Control (DDRPHYC) registers*/
+struct stm32mp1_ddrphy {
+ u32 ridr; /* 0x00 R Revision Identification*/
+ u32 pir; /* 0x04 R/W PHY Initialization*/
+ u32 pgcr; /* 0x08 R/W PHY General Configuration*/
+ u32 pgsr; /* 0x0C PHY General Status*/
+ u32 dllgcr; /* 0x10 R/W DLL General Control*/
+ u32 acdllcr; /* 0x14 R/W AC DLL Control*/
+ u32 ptr0; /* 0x18 R/W PHY Timing 0*/
+ u32 ptr1; /* 0x1C R/W PHY Timing 1*/
+ u32 ptr2; /* 0x20 R/W PHY Timing 2*/
+ u32 aciocr; /* 0x24 AC I/O Configuration*/
+ u32 dxccr; /* 0x28 DATX8 Common Configuration*/
+ u32 dsgcr; /* 0x2C DDR System General Configuration*/
+ u32 dcr; /* 0x30 DRAM Configuration*/
+ u32 dtpr0; /* 0x34 DRAM Timing Parameters0*/
+ u32 dtpr1; /* 0x38 DRAM Timing Parameters1*/
+ u32 dtpr2; /* 0x3C DRAM Timing Parameters2*/
+ u32 mr0; /* 0x40 Mode 0*/
+ u32 mr1; /* 0x44 Mode 1*/
+ u32 mr2; /* 0x48 Mode 2*/
+ u32 mr3; /* 0x4C Mode 3*/
+ u32 odtcr; /* 0x50 ODT Configuration*/
+ u32 dtar; /* 0x54 data training address*/
+ u32 dtdr0; /* 0x58 */
+ u32 dtdr1; /* 0x5c */
+ u8 res1[0x0c0 - 0x060]; /* 0x60 */
+ u32 dcuar; /* 0xc0 Address*/
+ u32 dcudr; /* 0xc4 DCU Data*/
+ u32 dcurr; /* 0xc8 DCU Run*/
+ u32 dculr; /* 0xcc DCU Loop*/
+ u32 dcugcr; /* 0xd0 DCU General Configuration*/
+ u32 dcutpr; /* 0xd4 DCU Timing Parameters */
+ u32 dcusr0; /* 0xd8 DCU Status 0*/
+ u32 dcusr1; /* 0xdc DCU Status 1*/
+ u8 res2[0x100 - 0xe0]; /* 0xe0 */
+ u32 bistrr; /* 0x100 BIST Run*/
+ u32 bistmskr0; /* 0x104 BIST Mask 0*/
+ u32 bistmskr1; /* 0x108 BIST Mask 0*/
+ u32 bistwcr; /* 0x10c BIST Word Count*/
+ u32 bistlsr; /* 0x110 BIST LFSR Seed*/
+ u32 bistar0; /* 0x114 BIST Address 0*/
+ u32 bistar1; /* 0x118 BIST Address 1*/
+ u32 bistar2; /* 0x11c BIST Address 2*/
+ u32 bistupdr; /* 0x120 BIST User Data Pattern*/
+ u32 bistgsr; /* 0x124 BIST General Status*/
+ u32 bistwer; /* 0x128 BIST Word Error*/
+ u32 bistber0; /* 0x12c BIST Bit Error 0*/
+ u32 bistber1; /* 0x130 BIST Bit Error 1*/
+ u32 bistber2; /* 0x134 BIST Bit Error 2*/
+ u32 bistwcsr; /* 0x138 BIST Word Count Status*/
+ u32 bistfwr0; /* 0x13c BIST Fail Word 0*/
+ u32 bistfwr1; /* 0x140 BIST Fail Word 1*/
+ u8 res3[0x178 - 0x144]; /* 0x144 */
+ u32 gpr0; /* 0x178 General Purpose 0 (GPR0)*/
+ u32 gpr1; /* 0x17C General Purpose 1 (GPR1)*/
+ u32 zq0cr0; /* 0x180 zq 0 control 0 */
+ u32 zq0cr1; /* 0x184 zq 0 control 1 */
+ u32 zq0sr0; /* 0x188 zq 0 status 0 */
+ u32 zq0sr1; /* 0x18C zq 0 status 1 */
+ u8 res4[0x1C0 - 0x190]; /* 0x190 */
+ u32 dx0gcr; /* 0x1c0 Byte lane 0 General Configuration*/
+ u32 dx0gsr0; /* 0x1c4 Byte lane 0 General Status 0*/
+ u32 dx0gsr1; /* 0x1c8 Byte lane 0 General Status 1*/
+ u32 dx0dllcr; /* 0x1cc Byte lane 0 DLL Control*/
+ u32 dx0dqtr; /* 0x1d0 Byte lane 0 DQ Timing*/
+ u32 dx0dqstr; /* 0x1d4 Byte lane 0 DQS Timing*/
+ u8 res5[0x200 - 0x1d8]; /* 0x1d8 */
+ u32 dx1gcr; /* 0x200 Byte lane 1 General Configuration*/
+ u32 dx1gsr0; /* 0x204 Byte lane 1 General Status 0*/
+ u32 dx1gsr1; /* 0x208 Byte lane 1 General Status 1*/
+ u32 dx1dllcr; /* 0x20c Byte lane 1 DLL Control*/
+ u32 dx1dqtr; /* 0x210 Byte lane 1 DQ Timing*/
+ u32 dx1dqstr; /* 0x214 Byte lane 1 QS Timing*/
+ u8 res6[0x240 - 0x218]; /* 0x218 */
+ u32 dx2gcr; /* 0x240 Byte lane 2 General Configuration*/
+ u32 dx2gsr0; /* 0x244 Byte lane 2 General Status 0*/
+ u32 dx2gsr1; /* 0x248 Byte lane 2 General Status 1*/
+ u32 dx2dllcr; /* 0x24c Byte lane 2 DLL Control*/
+ u32 dx2dqtr; /* 0x250 Byte lane 2 DQ Timing*/
+ u32 dx2dqstr; /* 0x254 Byte lane 2 QS Timing*/
+ u8 res7[0x280 - 0x258]; /* 0x258 */
+ u32 dx3gcr; /* 0x280 Byte lane 3 General Configuration*/
+ u32 dx3gsr0; /* 0x284 Byte lane 3 General Status 0*/
+ u32 dx3gsr1; /* 0x288 Byte lane 3 General Status 1*/
+ u32 dx3dllcr; /* 0x28c Byte lane 3 DLL Control*/
+ u32 dx3dqtr; /* 0x290 Byte lane 3 DQ Timing*/
+ u32 dx3dqstr; /* 0x294 Byte lane 3 QS Timing*/
+};
+
+#define DXN(phy, offset, byte) ((u32)(phy) + (offset) + ((u32)(byte) * 0x40))
+#define DXNGCR(phy, byte) DXN(phy, 0x1c0, byte)
+#define DXNDLLCR(phy, byte) DXN(phy, 0x1cc, byte)
+#define DXNDQTR(phy, byte) DXN(phy, 0x1d0, byte)
+#define DXNDQSTR(phy, byte) DXN(phy, 0x1d4, byte)
+
+/* DDRCTRL REGISTERS */
+#define DDRCTRL_MSTR_DDR3 BIT(0)
+#define DDRCTRL_MSTR_LPDDR2 BIT(2)
+#define DDRCTRL_MSTR_LPDDR3 BIT(3)
+#define DDRCTRL_MSTR_DATA_BUS_WIDTH_MASK GENMASK(13, 12)
+#define DDRCTRL_MSTR_DATA_BUS_WIDTH_FULL (0 << 12)
+#define DDRCTRL_MSTR_DATA_BUS_WIDTH_HALF (1 << 12)
+#define DDRCTRL_MSTR_DATA_BUS_WIDTH_QUARTER (2 << 12)
+#define DDRCTRL_MSTR_DLL_OFF_MODE BIT(15)
+
+#define DDRCTRL_STAT_OPERATING_MODE_MASK GENMASK(2, 0)
+#define DDRCTRL_STAT_OPERATING_MODE_NORMAL 1
+#define DDRCTRL_STAT_OPERATING_MODE_SR 3
+#define DDRCTRL_STAT_SELFREF_TYPE_MASK GENMASK(5, 4)
+#define DDRCTRL_STAT_SELFREF_TYPE_ASR (3 << 4)
+#define DDRCTRL_STAT_SELFREF_TYPE_SR (2 << 4)
+
+#define DDRCTRL_MRCTRL0_MR_TYPE_WRITE 0
+/* only one rank supported */
+#define DDRCTRL_MRCTRL0_MR_RANK_SHIFT 4
+#define DDRCTRL_MRCTRL0_MR_RANK_ALL \
+ (0x1 << DDRCTRL_MRCTRL0_MR_RANK_SHIFT)
+#define DDRCTRL_MRCTRL0_MR_ADDR_SHIFT 12
+#define DDRCTRL_MRCTRL0_MR_ADDR_MASK GENMASK(15, 12)
+#define DDRCTRL_MRCTRL0_MR_WR BIT(31)
+
+#define DDRCTRL_MRSTAT_MR_WR_BUSY BIT(0)
+
+#define DDRCTRL_PWRCTL_SELFREF_EN BIT(0)
+#define DDRCTRL_PWRCTL_POWERDOWN_EN BIT(1)
+#define DDRCTRL_PWRCTL_SELFREF_SW BIT(5)
+
+#define DDRCTRL_RFSHCTL3_DIS_AUTO_REFRESH BIT(0)
+
+#define DDRCTRL_RFSHTMG_T_RFC_NOM_X1_X32_MASK GENMASK(27, 16)
+#define DDRCTRL_RFSHTMG_T_RFC_NOM_X1_X32_SHIFT 16
+
+#define DDRCTRL_INIT0_SKIP_DRAM_INIT_MASK (0xC0000000)
+#define DDRCTRL_INIT0_SKIP_DRAM_INIT_NORMAL (BIT(30))
+
+#define DDRCTRL_DFIMISC_DFI_INIT_COMPLETE_EN BIT(0)
+
+#define DDRCTRL_DBG1_DIS_HIF BIT(1)
+
+#define DDRCTRL_DBGCAM_WR_DATA_PIPELINE_EMPTY BIT(29)
+#define DDRCTRL_DBGCAM_RD_DATA_PIPELINE_EMPTY BIT(28)
+#define DDRCTRL_DBGCAM_DBG_WR_Q_EMPTY BIT(26)
+#define DDRCTRL_DBGCAM_DBG_LPR_Q_DEPTH GENMASK(12, 8)
+#define DDRCTRL_DBGCAM_DBG_HPR_Q_DEPTH GENMASK(4, 0)
+#define DDRCTRL_DBGCAM_DATA_PIPELINE_EMPTY \
+ (DDRCTRL_DBGCAM_WR_DATA_PIPELINE_EMPTY | \
+ DDRCTRL_DBGCAM_RD_DATA_PIPELINE_EMPTY)
+#define DDRCTRL_DBGCAM_DBG_Q_DEPTH \
+ (DDRCTRL_DBGCAM_DBG_WR_Q_EMPTY | \
+ DDRCTRL_DBGCAM_DBG_LPR_Q_DEPTH | \
+ DDRCTRL_DBGCAM_DBG_HPR_Q_DEPTH)
+
+#define DDRCTRL_DBGCMD_RANK0_REFRESH BIT(0)
+
+#define DDRCTRL_DBGSTAT_RANK0_REFRESH_BUSY BIT(0)
+
+#define DDRCTRL_SWCTL_SW_DONE BIT(0)
+
+#define DDRCTRL_SWSTAT_SW_DONE_ACK BIT(0)
+
+#define DDRCTRL_PCTRL_N_PORT_EN BIT(0)
+
+/* DDRPHYC registers */
+#define DDRPHYC_PIR_INIT BIT(0)
+#define DDRPHYC_PIR_DLLSRST BIT(1)
+#define DDRPHYC_PIR_DLLLOCK BIT(2)
+#define DDRPHYC_PIR_ZCAL BIT(3)
+#define DDRPHYC_PIR_ITMSRST BIT(4)
+#define DDRPHYC_PIR_DRAMRST BIT(5)
+#define DDRPHYC_PIR_DRAMINIT BIT(6)
+#define DDRPHYC_PIR_QSTRN BIT(7)
+#define DDRPHYC_PIR_ICPC BIT(16)
+#define DDRPHYC_PIR_ZCALBYP BIT(30)
+#define DDRPHYC_PIR_INITSTEPS_MASK GENMASK(31, 7)
+
+#define DDRPHYC_PGCR_DFTCMP BIT(2)
+#define DDRPHYC_PGCR_PDDISDX BIT(24)
+#define DDRPHYC_PGCR_RFSHDT_MASK GENMASK(28, 25)
+
+#define DDRPHYC_PGSR_IDONE BIT(0)
+#define DDRPHYC_PGSR_DTERR BIT(5)
+#define DDRPHYC_PGSR_DTIERR BIT(6)
+#define DDRPHYC_PGSR_DFTERR BIT(7)
+#define DDRPHYC_PGSR_RVERR BIT(8)
+#define DDRPHYC_PGSR_RVEIRR BIT(9)
+
+#define DDRPHYC_DLLGCR_BPS200 BIT(23)
+
+#define DDRPHYC_ACDLLCR_DLLDIS BIT(31)
+
+#define DDRPHYC_ZQ0CRN_ZDATA_MASK GENMASK(27, 0)
+#define DDRPHYC_ZQ0CRN_ZDATA_SHIFT 0
+#define DDRPHYC_ZQ0CRN_ZDEN BIT(28)
+
+#define DDRPHYC_DXNGCR_DXEN BIT(0)
+
+#define DDRPHYC_DXNDLLCR_DLLSRST BIT(30)
+#define DDRPHYC_DXNDLLCR_DLLDIS BIT(31)
+#define DDRPHYC_DXNDLLCR_SDPHASE_MASK GENMASK(17, 14)
+#define DDRPHYC_DXNDLLCR_SDPHASE_SHIFT 14
+
+#define DDRPHYC_DXNDQTR_DQDLY_SHIFT(bit) (4 * (bit))
+#define DDRPHYC_DXNDQTR_DQDLY_MASK GENMASK(3, 0)
+#define DDRPHYC_DXNDQTR_DQDLY_LOW_MASK GENMASK(1, 0)
+#define DDRPHYC_DXNDQTR_DQDLY_HIGH_MASK GENMASK(3, 2)
+
+#define DDRPHYC_DXNDQSTR_DQSDLY_MASK GENMASK(22, 20)
+#define DDRPHYC_DXNDQSTR_DQSDLY_SHIFT 20
+#define DDRPHYC_DXNDQSTR_DQSNDLY_MASK GENMASK(25, 23)
+#define DDRPHYC_DXNDQSTR_DQSNDLY_SHIFT 23
+#define DDRPHYC_DXNDQSTR_R0DGSL_MASK GENMASK(2, 0)
+#define DDRPHYC_DXNDQSTR_R0DGSL_SHIFT 0
+#define DDRPHYC_DXNDQSTR_R0DGPS_MASK GENMASK(13, 12)
+#define DDRPHYC_DXNDQSTR_R0DGPS_SHIFT 12
+
+#define DDRPHYC_BISTRR_BDXSEL_MASK GENMASK(22, 19)
+#define DDRPHYC_BISTRR_BDXSEL_SHIFT 19
+
+#define DDRPHYC_BISTGSR_BDDONE BIT(0)
+#define DDRPHYC_BISTGSR_BDXERR BIT(2)
+
+#define DDRPHYC_BISTWCSR_DXWCNT_SHIFT 16
+
+/* PWR registers */
+#define PWR_CR3 0x00C
+#define PWR_CR3_DDRSRDIS BIT(11)
+#define PWR_CR3_DDRRETEN BIT(12)
+
+#endif
diff --git a/roms/u-boot/drivers/ram/stm32mp1/stm32mp1_interactive.c b/roms/u-boot/drivers/ram/stm32mp1/stm32mp1_interactive.c
new file mode 100644
index 000000000..8c2310ac9
--- /dev/null
+++ b/roms/u-boot/drivers/ram/stm32mp1/stm32mp1_interactive.c
@@ -0,0 +1,497 @@
+// 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_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",
+#ifdef CONFIG_STM32MP1_DDR_TESTS
+ [DDR_CMD_TEST] = "test",
+#endif
+#ifdef CONFIG_STM32MP1_DDR_TUNING
+ [DDR_CMD_TUNING] = "tuning",
+#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
+#ifdef CONFIG_STM32MP1_DDR_TUNING
+ [DDR_CMD_TUNING] = { 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, speed or cal\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
+#ifdef CONFIG_STM32MP1_DDR_TUNING
+ "tuning [help] | <n> [...] lists (with help) or execute tuning <n>\n"
+#endif
+ "\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);
+ printf("cal = %d\n", config->p_cal_present);
+ 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;
+ }
+ if (!strcmp(argv[1], "cal")) {
+ if (strict_strtoul(argv[2], 10, &value) < 0 ||
+ (value != 0 && value != 1)) {
+ printf("invalid value %s\n", argv[2]);
+ } else {
+ config->p_cal_present = value;
+ printf("cal = %d\n", config->p_cal_present);
+ }
+ 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) || defined(CONFIG_STM32MP1_DDR_TUNING)
+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
+
+#ifdef CONFIG_STM32MP1_DDR_TUNING
+ case DDR_CMD_TUNING:
+ if (!stm32mp1_check_step(step, STEP_DDR_READY))
+ continue;
+ stm32mp1_ddr_subcmd(priv, argc, argv,
+ tuning, tuning_nb);
+ break;
+#endif
+
+ default:
+ break;
+ }
+ }
+ return next_step == STEP_DDR_RESET;
+}
diff --git a/roms/u-boot/drivers/ram/stm32mp1/stm32mp1_ram.c b/roms/u-boot/drivers/ram/stm32mp1/stm32mp1_ram.c
new file mode 100644
index 000000000..26f0b4f1e
--- /dev/null
+++ b/roms/u-boot/drivers/ram/stm32mp1/stm32mp1_ram.c
@@ -0,0 +1,243 @@
+// SPDX-License-Identifier: GPL-2.0+ OR BSD-3-Clause
+/*
+ * Copyright (C) 2018, STMicroelectronics - All Rights Reserved
+ */
+
+#define LOG_CATEGORY UCLASS_RAM
+
+#include <common.h>
+#include <clk.h>
+#include <dm.h>
+#include <init.h>
+#include <log.h>
+#include <ram.h>
+#include <regmap.h>
+#include <syscon.h>
+#include <asm/io.h>
+#include <dm/device_compat.h>
+#include "stm32mp1_ddr.h"
+
+static const char *const clkname[] = {
+ "ddrc1",
+ "ddrc2",
+ "ddrcapb",
+ "ddrphycapb",
+ "ddrphyc" /* LAST clock => used for get_rate() */
+};
+
+int stm32mp1_ddr_clk_enable(struct ddr_info *priv, uint32_t mem_speed)
+{
+ unsigned long ddrphy_clk;
+ unsigned long ddr_clk;
+ struct clk clk;
+ int ret;
+ unsigned int idx;
+
+ for (idx = 0; idx < ARRAY_SIZE(clkname); idx++) {
+ ret = clk_get_by_name(priv->dev, clkname[idx], &clk);
+
+ if (!ret)
+ ret = clk_enable(&clk);
+
+ if (ret) {
+ log_err("error for %s : %d\n", clkname[idx], ret);
+ return ret;
+ }
+ }
+
+ priv->clk = clk;
+ ddrphy_clk = clk_get_rate(&priv->clk);
+
+ log_debug("DDR: mem_speed (%d kHz), RCC %d kHz\n",
+ mem_speed, (u32)(ddrphy_clk / 1000));
+ /* max 10% frequency delta */
+ ddr_clk = abs(ddrphy_clk - mem_speed * 1000);
+ if (ddr_clk > (mem_speed * 100)) {
+ log_err("DDR expected freq %d kHz, current is %d kHz\n",
+ mem_speed, (u32)(ddrphy_clk / 1000));
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+__weak int board_stm32mp1_ddr_config_name_match(struct udevice *dev,
+ const char *name)
+{
+ return 0; /* Always match */
+}
+
+static ofnode stm32mp1_ddr_get_ofnode(struct udevice *dev)
+{
+ const char *name;
+ ofnode node;
+
+ dev_for_each_subnode(node, dev) {
+ name = ofnode_get_property(node, "compatible", NULL);
+
+ if (!board_stm32mp1_ddr_config_name_match(dev, name))
+ return node;
+ }
+
+ return dev_ofnode(dev);
+}
+
+static __maybe_unused int stm32mp1_ddr_setup(struct udevice *dev)
+{
+ struct ddr_info *priv = dev_get_priv(dev);
+ int ret;
+ unsigned int idx;
+ struct clk axidcg;
+ struct stm32mp1_ddr_config config;
+ ofnode node = stm32mp1_ddr_get_ofnode(dev);
+
+#define PARAM(x, y, z) \
+ { .name = x, \
+ .offset = offsetof(struct stm32mp1_ddr_config, y), \
+ .size = sizeof(config.y) / sizeof(u32), \
+ .present = z, \
+ }
+
+#define CTL_PARAM(x) PARAM("st,ctl-"#x, c_##x, NULL)
+#define PHY_PARAM(x) PARAM("st,phy-"#x, p_##x, NULL)
+#define PHY_PARAM_OPT(x) PARAM("st,phy-"#x, p_##x, &config.p_##x##_present)
+
+ const struct {
+ const char *name; /* name in DT */
+ const u32 offset; /* offset in config struct */
+ const u32 size; /* size of parameters */
+ bool * const present; /* presence indication for opt */
+ } param[] = {
+ CTL_PARAM(reg),
+ CTL_PARAM(timing),
+ CTL_PARAM(map),
+ CTL_PARAM(perf),
+ PHY_PARAM(reg),
+ PHY_PARAM(timing),
+ PHY_PARAM_OPT(cal)
+ };
+
+ config.info.speed = ofnode_read_u32_default(node, "st,mem-speed", 0);
+ config.info.size = ofnode_read_u32_default(node, "st,mem-size", 0);
+ config.info.name = ofnode_read_string(node, "st,mem-name");
+ if (!config.info.name) {
+ dev_dbg(dev, "no st,mem-name\n");
+ return -EINVAL;
+ }
+ printf("RAM: %s\n", config.info.name);
+
+ for (idx = 0; idx < ARRAY_SIZE(param); idx++) {
+ ret = ofnode_read_u32_array(node, param[idx].name,
+ (void *)((u32)&config +
+ param[idx].offset),
+ param[idx].size);
+ dev_dbg(dev, "%s: %s[0x%x] = %d\n", __func__,
+ param[idx].name, param[idx].size, ret);
+ if (ret &&
+ (ret != -FDT_ERR_NOTFOUND || !param[idx].present)) {
+ dev_err(dev, "Cannot read %s, error=%d\n",
+ param[idx].name, ret);
+ return -EINVAL;
+ }
+ if (param[idx].present) {
+ /* save presence of optional parameters */
+ *param[idx].present = true;
+ if (ret == -FDT_ERR_NOTFOUND) {
+ *param[idx].present = false;
+#ifdef CONFIG_STM32MP1_DDR_INTERACTIVE
+ /* reset values if used later */
+ memset((void *)((u32)&config +
+ param[idx].offset),
+ 0, param[idx].size * sizeof(u32));
+#endif
+ }
+ }
+ }
+
+ ret = clk_get_by_name(dev, "axidcg", &axidcg);
+ if (ret) {
+ dev_dbg(dev, "%s: Cannot found axidcg\n", __func__);
+ return -EINVAL;
+ }
+ clk_disable(&axidcg); /* disable clock gating during init */
+
+ stm32mp1_ddr_init(priv, &config);
+
+ clk_enable(&axidcg); /* enable clock gating */
+
+ /* check size */
+ dev_dbg(dev, "get_ram_size(%x, %x)\n",
+ (u32)priv->info.base, (u32)STM32_DDR_SIZE);
+
+ priv->info.size = get_ram_size((long *)priv->info.base,
+ STM32_DDR_SIZE);
+
+ dev_dbg(dev, "info.size: %x\n", (u32)priv->info.size);
+
+ /* check memory access for all memory */
+ if (config.info.size != priv->info.size) {
+ printf("DDR invalid size : 0x%x, expected 0x%x\n",
+ priv->info.size, config.info.size);
+ return -EINVAL;
+ }
+ return 0;
+}
+
+static int stm32mp1_ddr_probe(struct udevice *dev)
+{
+ struct ddr_info *priv = dev_get_priv(dev);
+ struct regmap *map;
+ int ret;
+
+ priv->dev = dev;
+
+ ret = regmap_init_mem(dev_ofnode(dev), &map);
+ if (ret)
+ return log_ret(ret);
+
+ priv->ctl = regmap_get_range(map, 0);
+ priv->phy = regmap_get_range(map, 1);
+
+ priv->rcc = STM32_RCC_BASE;
+
+ priv->info.base = STM32_DDR_BASE;
+
+#if !defined(CONFIG_TFABOOT) && \
+ (!defined(CONFIG_SPL) || defined(CONFIG_SPL_BUILD))
+ priv->info.size = 0;
+ ret = stm32mp1_ddr_setup(dev);
+
+ return log_ret(ret);
+#else
+ ofnode node = stm32mp1_ddr_get_ofnode(dev);
+ priv->info.size = ofnode_read_u32_default(node, "st,mem-size", 0);
+ return 0;
+#endif
+}
+
+static int stm32mp1_ddr_get_info(struct udevice *dev, struct ram_info *info)
+{
+ struct ddr_info *priv = dev_get_priv(dev);
+
+ *info = priv->info;
+
+ return 0;
+}
+
+static struct ram_ops stm32mp1_ddr_ops = {
+ .get_info = stm32mp1_ddr_get_info,
+};
+
+static const struct udevice_id stm32mp1_ddr_ids[] = {
+ { .compatible = "st,stm32mp1-ddr" },
+ { }
+};
+
+U_BOOT_DRIVER(ddr_stm32mp1) = {
+ .name = "stm32mp1_ddr",
+ .id = UCLASS_RAM,
+ .of_match = stm32mp1_ddr_ids,
+ .ops = &stm32mp1_ddr_ops,
+ .probe = stm32mp1_ddr_probe,
+ .priv_auto = sizeof(struct ddr_info),
+};
diff --git a/roms/u-boot/drivers/ram/stm32mp1/stm32mp1_tests.c b/roms/u-boot/drivers/ram/stm32mp1/stm32mp1_tests.c
new file mode 100644
index 000000000..64262f1aa
--- /dev/null
+++ b/roms/u-boot/drivers/ram/stm32mp1/stm32mp1_tests.c
@@ -0,0 +1,1504 @@
+// 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 <console.h>
+#include <init.h>
+#include <log.h>
+#include <rand.h>
+#include <watchdog.h>
+#include <asm/global_data.h>
+#include <asm/io.h>
+#include <linux/log2.h>
+#include "stm32mp1_tests.h"
+
+#define ADDR_INVALID 0xFFFFFFFF
+
+#define PATTERN_DEFAULT "-"
+
+DECLARE_GLOBAL_DATA_PTR;
+
+static int get_bufsize(char *string, int argc, char *argv[], int arg_nb,
+ size_t *bufsize, size_t default_size, size_t min_size)
+{
+ unsigned long value;
+
+ if (argc > arg_nb) {
+ if (strict_strtoul(argv[arg_nb], 0, &value) < 0) {
+ sprintf(string, "invalid %d parameter %s",
+ arg_nb, argv[arg_nb]);
+ return -1;
+ }
+ if (value > STM32_DDR_SIZE || value < min_size) {
+ sprintf(string, "invalid size %s (min=%d)",
+ argv[arg_nb], min_size);
+ return -1;
+ }
+ if (value & (min_size - 1)) {
+ sprintf(string, "unaligned size %s (min=%d)",
+ argv[arg_nb], min_size);
+ return -1;
+ }
+ *bufsize = value;
+ } else {
+ if (default_size != STM32_DDR_SIZE)
+ *bufsize = default_size;
+ else
+ *bufsize = get_ram_size((long *)STM32_DDR_BASE,
+ STM32_DDR_SIZE);
+ }
+ return 0;
+}
+
+static int get_nb_loop(char *string, int argc, char *argv[], int arg_nb,
+ u32 *nb_loop, u32 default_nb_loop)
+{
+ unsigned long value;
+
+ if (argc > arg_nb) {
+ if (strict_strtoul(argv[arg_nb], 0, &value) < 0) {
+ sprintf(string, "invalid %d parameter %s",
+ arg_nb, argv[arg_nb]);
+ return -1;
+ }
+ if (value == 0)
+ printf("WARNING: infinite loop requested\n");
+ *nb_loop = value;
+ } else {
+ *nb_loop = default_nb_loop;
+ }
+
+ return 0;
+}
+
+static int get_addr(char *string, int argc, char *argv[], int arg_nb,
+ u32 *addr)
+{
+ unsigned long value;
+
+ if (argc > arg_nb) {
+ if (strict_strtoul(argv[arg_nb], 16, &value) < 0) {
+ sprintf(string, "invalid %d parameter %s",
+ arg_nb, argv[arg_nb]);
+ return -1;
+ }
+ if (value < STM32_DDR_BASE) {
+ sprintf(string, "too low address %s", argv[arg_nb]);
+ return -1;
+ }
+ if (value & 0x3 && value != ADDR_INVALID) {
+ sprintf(string, "unaligned address %s",
+ argv[arg_nb]);
+ return -1;
+ }
+ *addr = value;
+ } else {
+ *addr = STM32_DDR_BASE;
+ }
+
+ return 0;
+}
+
+static int get_pattern(char *string, int argc, char *argv[], int arg_nb,
+ u32 *pattern, u32 default_pattern)
+{
+ unsigned long value;
+
+ if (argc > arg_nb) {
+ if (!strcmp(argv[arg_nb], PATTERN_DEFAULT)) {
+ *pattern = default_pattern;
+ return 0;
+ }
+ if (strict_strtoul(argv[arg_nb], 16, &value) < 0) {
+ sprintf(string, "invalid %d parameter %s",
+ arg_nb, argv[arg_nb]);
+ return -1;
+ }
+ *pattern = value;
+ } else {
+ *pattern = default_pattern;
+ }
+
+ return 0;
+}
+
+static u32 check_addr(u32 addr, u32 value)
+{
+ u32 data = readl(addr);
+
+ if (value != data) {
+ printf("0x%08x: 0x%08x <=> 0x%08x", addr, data, value);
+ data = readl(addr);
+ printf("(2nd read: 0x%08x)", data);
+ if (value == data)
+ printf("- read error");
+ else
+ printf("- write error");
+ printf("\n");
+ return -1;
+ }
+ return 0;
+}
+
+static int progress(u32 offset)
+{
+ if (!(offset & 0xFFFFFF)) {
+ putc('.');
+ if (ctrlc()) {
+ printf("\ntest interrupted!\n");
+ return 1;
+ }
+ }
+ return 0;
+}
+
+static int test_loop_end(u32 *loop, u32 nb_loop, u32 progress)
+{
+ (*loop)++;
+ if (nb_loop && *loop >= nb_loop)
+ return 1;
+ if ((*loop) % progress)
+ return 0;
+ /* allow to interrupt the test only for progress step */
+ if (ctrlc()) {
+ printf("test interrupted!\n");
+ return 1;
+ }
+ printf("loop #%d\n", *loop);
+ WATCHDOG_RESET();
+
+ return 0;
+}
+
+/**********************************************************************
+ *
+ * Function: memTestDataBus()
+ *
+ * Description: Test the data bus wiring in a memory region by
+ * performing a walking 1's test at a fixed address
+ * within that region. The address is selected
+ * by the caller.
+ *
+ * Notes:
+ *
+ * Returns: 0 if the test succeeds.
+ * A non-zero result is the first pattern that failed.
+ *
+ **********************************************************************/
+static u32 databus(u32 *address)
+{
+ u32 pattern;
+ u32 read_value;
+
+ /* Perform a walking 1's test at the given address. */
+ for (pattern = 1; pattern != 0; pattern <<= 1) {
+ /* Write the test pattern. */
+ writel(pattern, address);
+
+ /* Read it back (immediately is okay for this test). */
+ read_value = readl(address);
+ log_debug("%x: %x <=> %x\n",
+ (u32)address, read_value, pattern);
+
+ if (read_value != pattern)
+ return pattern;
+ }
+
+ return 0;
+}
+
+/**********************************************************************
+ *
+ * Function: memTestAddressBus()
+ *
+ * Description: Test the address bus wiring in a memory region by
+ * performing a walking 1's test on the relevant bits
+ * of the address and checking for aliasing. This test
+ * will find single-bit address failures such as stuck
+ * -high, stuck-low, and shorted pins. The base address
+ * and size of the region are selected by the caller.
+ *
+ * Notes: For best results, the selected base address should
+ * have enough LSB 0's to guarantee single address bit
+ * changes. For example, to test a 64-Kbyte region,
+ * select a base address on a 64-Kbyte boundary. Also,
+ * select the region size as a power-of-two--if at all
+ * possible.
+ *
+ * Returns: NULL if the test succeeds.
+ * A non-zero result is the first address at which an
+ * aliasing problem was uncovered. By examining the
+ * contents of memory, it may be possible to gather
+ * additional information about the problem.
+ *
+ **********************************************************************/
+static u32 *addressbus(u32 *address, u32 nb_bytes)
+{
+ u32 mask = (nb_bytes / sizeof(u32) - 1);
+ u32 offset;
+ u32 test_offset;
+ u32 read_value;
+
+ u32 pattern = 0xAAAAAAAA;
+ u32 antipattern = 0x55555555;
+
+ /* Write the default pattern at each of the power-of-two offsets. */
+ for (offset = 1; (offset & mask) != 0; offset <<= 1)
+ writel(pattern, &address[offset]);
+
+ /* Check for address bits stuck high. */
+ test_offset = 0;
+ writel(antipattern, &address[test_offset]);
+
+ for (offset = 1; (offset & mask) != 0; offset <<= 1) {
+ read_value = readl(&address[offset]);
+ log_debug("%x: %x <=> %x\n",
+ (u32)&address[offset], read_value, pattern);
+ if (read_value != pattern)
+ return &address[offset];
+ }
+
+ writel(pattern, &address[test_offset]);
+
+ /* Check for address bits stuck low or shorted. */
+ for (test_offset = 1; (test_offset & mask) != 0; test_offset <<= 1) {
+ writel(antipattern, &address[test_offset]);
+ if (readl(&address[0]) != pattern)
+ return &address[test_offset];
+
+ for (offset = 1; (offset & mask) != 0; offset <<= 1) {
+ if (readl(&address[offset]) != pattern &&
+ offset != test_offset)
+ return &address[test_offset];
+ }
+ writel(pattern, &address[test_offset]);
+ }
+
+ return NULL;
+}
+
+/**********************************************************************
+ *
+ * Function: memTestDevice()
+ *
+ * Description: Test the integrity of a physical memory device by
+ * performing an increment/decrement test over the
+ * entire region. In the process every storage bit
+ * in the device is tested as a zero and a one. The
+ * base address and the size of the region are
+ * selected by the caller.
+ *
+ * Notes:
+ *
+ * Returns: NULL if the test succeeds.
+ *
+ * A non-zero result is the first address at which an
+ * incorrect value was read back. By examining the
+ * contents of memory, it may be possible to gather
+ * additional information about the problem.
+ *
+ **********************************************************************/
+static u32 *memdevice(u32 *address, u32 nb_bytes)
+{
+ u32 offset;
+ u32 nb_words = nb_bytes / sizeof(u32);
+
+ u32 pattern;
+ u32 antipattern;
+
+ puts("Fill with pattern");
+ /* Fill memory with a known pattern. */
+ for (pattern = 1, offset = 0; offset < nb_words; pattern++, offset++) {
+ writel(pattern, &address[offset]);
+ if (progress(offset))
+ return NULL;
+ }
+
+ puts("\nCheck and invert pattern");
+ /* Check each location and invert it for the second pass. */
+ for (pattern = 1, offset = 0; offset < nb_words; pattern++, offset++) {
+ if (readl(&address[offset]) != pattern)
+ return &address[offset];
+
+ antipattern = ~pattern;
+ writel(antipattern, &address[offset]);
+ if (progress(offset))
+ return NULL;
+ }
+
+ puts("\nCheck inverted pattern");
+ /* Check each location for the inverted pattern and zero it. */
+ for (pattern = 1, offset = 0; offset < nb_words; pattern++, offset++) {
+ antipattern = ~pattern;
+ if (readl(&address[offset]) != antipattern)
+ return &address[offset];
+ if (progress(offset))
+ return NULL;
+ }
+ printf("\n");
+
+ return NULL;
+}
+
+static enum test_result databuswalk0(struct stm32mp1_ddrctl *ctl,
+ struct stm32mp1_ddrphy *phy,
+ char *string, int argc, char *argv[])
+{
+ int i;
+ u32 loop = 0, nb_loop;
+ u32 addr;
+ u32 error = 0;
+ u32 data;
+
+ if (get_nb_loop(string, argc, argv, 0, &nb_loop, 100))
+ return TEST_ERROR;
+ if (get_addr(string, argc, argv, 1, &addr))
+ return TEST_ERROR;
+
+ printf("running %d loops at 0x%x\n", nb_loop, addr);
+ while (!error) {
+ for (i = 0; i < 32; i++)
+ writel(~(1 << i), addr + 4 * i);
+ for (i = 0; i < 32; i++) {
+ data = readl(addr + 4 * i);
+ if (~(1 << i) != data) {
+ error |= 1 << i;
+ log_debug("%x: error %x expected %x => error:%x\n",
+ addr + 4 * i, data, ~(1 << i), error);
+ }
+ }
+ if (test_loop_end(&loop, nb_loop, 1000))
+ break;
+ for (i = 0; i < 32; i++)
+ writel(0, addr + 4 * i);
+ }
+ if (error) {
+ sprintf(string, "loop %d: error for bits 0x%x",
+ loop, error);
+ return TEST_FAILED;
+ }
+ sprintf(string, "no error for %d loops", loop);
+ return TEST_PASSED;
+}
+
+static enum test_result databuswalk1(struct stm32mp1_ddrctl *ctl,
+ struct stm32mp1_ddrphy *phy,
+ char *string, int argc, char *argv[])
+{
+ int i;
+ u32 loop = 0, nb_loop;
+ u32 addr;
+ u32 error = 0;
+ u32 data;
+
+ if (get_nb_loop(string, argc, argv, 0, &nb_loop, 100))
+ return TEST_ERROR;
+ if (get_addr(string, argc, argv, 1, &addr))
+ return TEST_ERROR;
+ printf("running %d loops at 0x%x\n", nb_loop, addr);
+ while (!error) {
+ for (i = 0; i < 32; i++)
+ writel(1 << i, addr + 4 * i);
+ for (i = 0; i < 32; i++) {
+ data = readl(addr + 4 * i);
+ if ((1 << i) != data) {
+ error |= 1 << i;
+ log_debug("%x: error %x expected %x => error:%x\n",
+ addr + 4 * i, data, (1 << i), error);
+ }
+ }
+ if (test_loop_end(&loop, nb_loop, 1000))
+ break;
+ for (i = 0; i < 32; i++)
+ writel(0, addr + 4 * i);
+ }
+ if (error) {
+ sprintf(string, "loop %d: error for bits 0x%x",
+ loop, error);
+ return TEST_FAILED;
+ }
+ sprintf(string, "no error for %d loops", loop);
+ return TEST_PASSED;
+}
+
+static enum test_result test_databus(struct stm32mp1_ddrctl *ctl,
+ struct stm32mp1_ddrphy *phy,
+ char *string, int argc, char *argv[])
+{
+ u32 addr;
+ u32 error;
+
+ if (get_addr(string, argc, argv, 0, &addr))
+ return TEST_ERROR;
+ error = databus((u32 *)addr);
+ if (error) {
+ sprintf(string, "0x%x: error for bits 0x%x",
+ addr, error);
+ return TEST_FAILED;
+ }
+ sprintf(string, "address 0x%x", addr);
+ return TEST_PASSED;
+}
+
+static enum test_result test_addressbus(struct stm32mp1_ddrctl *ctl,
+ struct stm32mp1_ddrphy *phy,
+ char *string, int argc, char *argv[])
+{
+ u32 addr;
+ u32 bufsize;
+ u32 error;
+
+ if (get_bufsize(string, argc, argv, 0, &bufsize, STM32_DDR_SIZE, 4))
+ return TEST_ERROR;
+ if (!is_power_of_2(bufsize)) {
+ sprintf(string, "size 0x%x is not a power of 2",
+ (u32)bufsize);
+ return TEST_ERROR;
+ }
+ if (get_addr(string, argc, argv, 1, &addr))
+ return TEST_ERROR;
+
+ printf("running at 0x%08x length 0x%x\n", addr, bufsize);
+ error = (u32)addressbus((u32 *)addr, bufsize);
+ if (error) {
+ sprintf(string, "0x%x: error for address 0x%x",
+ addr, error);
+ return TEST_FAILED;
+ }
+ sprintf(string, "address 0x%x, size 0x%x",
+ addr, bufsize);
+ return TEST_PASSED;
+}
+
+static enum test_result test_memdevice(struct stm32mp1_ddrctl *ctl,
+ struct stm32mp1_ddrphy *phy,
+ char *string, int argc, char *argv[])
+{
+ u32 addr;
+ size_t bufsize;
+ u32 error;
+
+ if (get_bufsize(string, argc, argv, 0, &bufsize, 4 * 1024, 4))
+ return TEST_ERROR;
+ if (get_addr(string, argc, argv, 1, &addr))
+ return TEST_ERROR;
+ error = (u32)memdevice((u32 *)addr, (unsigned long)bufsize);
+ if (error) {
+ sprintf(string, "0x%x: error for address 0x%x",
+ addr, error);
+ return TEST_FAILED;
+ }
+ sprintf(string, "address 0x%x, size 0x%x",
+ addr, bufsize);
+ return TEST_PASSED;
+}
+
+/**********************************************************************
+ *
+ * Function: sso
+ *
+ * Description: Test the Simultaneous Switching Output.
+ * Verifies succes sive reads and writes to the same memory word,
+ * holding one bit constant while toggling all other data bits
+ * simultaneously
+ * => stress the data bus over an address range
+ *
+ * The CPU writes to each address in the given range.
+ * For each bit, first the CPU holds the bit at 1 while
+ * toggling the other bits, and then the CPU holds the bit at 0
+ * while toggling the other bits.
+ * After each write, the CPU reads the address that was written
+ * to verify that it contains the correct data
+ *
+ **********************************************************************/
+static enum test_result test_sso(struct stm32mp1_ddrctl *ctl,
+ struct stm32mp1_ddrphy *phy,
+ char *string, int argc, char *argv[])
+{
+ int i, j;
+ u32 addr, bufsize, remaining, offset;
+ u32 error = 0;
+ u32 data;
+
+ if (get_bufsize(string, argc, argv, 0, &bufsize, 4 * 1024, 4))
+ return TEST_ERROR;
+ if (get_addr(string, argc, argv, 1, &addr))
+ return TEST_ERROR;
+
+ printf("running sso at 0x%x length 0x%x", addr, bufsize);
+ offset = addr;
+ remaining = bufsize;
+ while (remaining) {
+ for (i = 0; i < 32; i++) {
+ /* write pattern. */
+ for (j = 0; j < 6; j++) {
+ switch (j) {
+ case 0:
+ case 2:
+ data = 1 << i;
+ break;
+ case 3:
+ case 5:
+ data = ~(1 << i);
+ break;
+ case 1:
+ data = ~0x0;
+ break;
+ case 4:
+ data = 0x0;
+ break;
+ }
+
+ writel(data, offset);
+ error = check_addr(offset, data);
+ if (error)
+ goto end;
+ }
+ }
+ offset += 4;
+ remaining -= 4;
+ if (progress(offset << 7))
+ goto end;
+ }
+ puts("\n");
+
+end:
+ if (error) {
+ sprintf(string, "error for pattern 0x%x @0x%x",
+ data, offset);
+ return TEST_FAILED;
+ }
+ sprintf(string, "no error for sso at 0x%x length 0x%x", addr, bufsize);
+ return TEST_PASSED;
+}
+
+/**********************************************************************
+ *
+ * Function: Random
+ *
+ * Description: Verifies r/w with pseudo-ramdom value on one region
+ * + write the region (individual access)
+ * + memcopy to the 2nd region (try to use burst)
+ * + verify the 2 regions
+ *
+ **********************************************************************/
+static enum test_result test_random(struct stm32mp1_ddrctl *ctl,
+ struct stm32mp1_ddrphy *phy,
+ char *string, int argc, char *argv[])
+{
+ u32 addr, offset, value = 0;
+ size_t bufsize;
+ u32 loop = 0, nb_loop;
+ u32 error = 0;
+ unsigned int seed;
+
+ if (get_bufsize(string, argc, argv, 0, &bufsize, 4 * 1024, 8))
+ return TEST_ERROR;
+ if (get_nb_loop(string, argc, argv, 1, &nb_loop, 1))
+ return TEST_ERROR;
+ if (get_addr(string, argc, argv, 2, &addr))
+ return TEST_ERROR;
+
+ bufsize /= 2;
+ printf("running %d loops copy from 0x%x to 0x%x (buffer size=0x%x)\n",
+ nb_loop, addr, addr + bufsize, bufsize);
+ while (!error) {
+ seed = rand();
+ for (offset = 0; offset < bufsize; offset += 4)
+ writel(rand(), addr + offset);
+
+ memcpy((void *)addr + bufsize, (void *)addr, bufsize);
+
+ srand(seed);
+ for (offset = 0; offset < 2 * bufsize; offset += 4) {
+ if (offset == bufsize)
+ srand(seed);
+ value = rand();
+ error = check_addr(addr + offset, value);
+ if (error)
+ break;
+ if (progress(offset))
+ return TEST_FAILED;
+ }
+ if (test_loop_end(&loop, nb_loop, 100))
+ break;
+ }
+ putc('\n');
+
+ if (error) {
+ sprintf(string,
+ "loop %d: error for address 0x%x: 0x%x expected 0x%x",
+ loop, offset, readl(offset), value);
+ return TEST_FAILED;
+ }
+ sprintf(string, "no error for %d loops, size 0x%x",
+ loop, bufsize);
+ return TEST_PASSED;
+}
+
+/**********************************************************************
+ *
+ * Function: noise
+ *
+ * Description: Verifies r/w while forcing switching of all data bus lines.
+ * optimised 4 iteration write/read/write/read cycles...
+ * for pattern and inversed pattern
+ *
+ **********************************************************************/
+void do_noise(u32 addr, u32 pattern, u32 *result)
+{
+ __asm__("push {R0-R11}");
+ __asm__("mov r0, %0" : : "r" (addr));
+ __asm__("mov r1, %0" : : "r" (pattern));
+ __asm__("mov r11, %0" : : "r" (result));
+
+ __asm__("mvn r2, r1");
+
+ __asm__("str r1, [r0]");
+ __asm__("ldr r3, [r0]");
+ __asm__("str r2, [r0]");
+ __asm__("ldr r4, [r0]");
+
+ __asm__("str r1, [r0]");
+ __asm__("ldr r5, [r0]");
+ __asm__("str r2, [r0]");
+ __asm__("ldr r6, [r0]");
+
+ __asm__("str r1, [r0]");
+ __asm__("ldr r7, [r0]");
+ __asm__("str r2, [r0]");
+ __asm__("ldr r8, [r0]");
+
+ __asm__("str r1, [r0]");
+ __asm__("ldr r9, [r0]");
+ __asm__("str r2, [r0]");
+ __asm__("ldr r10, [r0]");
+
+ __asm__("stmia R11!, {R3-R10}");
+
+ __asm__("pop {R0-R11}");
+}
+
+static enum test_result test_noise(struct stm32mp1_ddrctl *ctl,
+ struct stm32mp1_ddrphy *phy,
+ char *string, int argc, char *argv[])
+{
+ u32 addr, pattern;
+ u32 result[8];
+ int i;
+ enum test_result res = TEST_PASSED;
+
+ if (get_pattern(string, argc, argv, 0, &pattern, 0xFFFFFFFF))
+ return TEST_ERROR;
+ if (get_addr(string, argc, argv, 1, &addr))
+ return TEST_ERROR;
+
+ printf("running noise for 0x%x at 0x%x\n", pattern, addr);
+
+ do_noise(addr, pattern, result);
+
+ for (i = 0; i < 0x8;) {
+ if (check_addr((u32)&result[i++], pattern))
+ res = TEST_FAILED;
+ if (check_addr((u32)&result[i++], ~pattern))
+ res = TEST_FAILED;
+ }
+
+ return res;
+}
+
+/**********************************************************************
+ *
+ * Function: noise_burst
+ *
+ * Description: Verifies r/w while forcing switching of all data bus lines.
+ * optimised write loop witrh store multiple to use burst
+ * for pattern and inversed pattern
+ *
+ **********************************************************************/
+void do_noise_burst(u32 addr, u32 pattern, size_t bufsize)
+{
+ __asm__("push {R0-R9}");
+ __asm__("mov r0, %0" : : "r" (addr));
+ __asm__("mov r1, %0" : : "r" (pattern));
+ __asm__("mov r9, %0" : : "r" (bufsize));
+
+ __asm__("mvn r2, r1");
+ __asm__("mov r3, r1");
+ __asm__("mov r4, r2");
+ __asm__("mov r5, r1");
+ __asm__("mov r6, r2");
+ __asm__("mov r7, r1");
+ __asm__("mov r8, r2");
+
+ __asm__("loop1:");
+ __asm__("stmia R0!, {R1-R8}");
+ __asm__("stmia R0!, {R1-R8}");
+ __asm__("stmia R0!, {R1-R8}");
+ __asm__("stmia R0!, {R1-R8}");
+ __asm__("subs r9, r9, #128");
+ __asm__("bge loop1");
+ __asm__("pop {R0-R9}");
+}
+
+/* chunk size enough to allow interruption with Ctrl-C*/
+#define CHUNK_SIZE 0x8000000
+static enum test_result test_noise_burst(struct stm32mp1_ddrctl *ctl,
+ struct stm32mp1_ddrphy *phy,
+ char *string, int argc, char *argv[])
+{
+ u32 addr, offset, pattern;
+ size_t bufsize, remaining, size;
+ int i;
+ enum test_result res = TEST_PASSED;
+
+ if (get_bufsize(string, argc, argv, 0, &bufsize, 4 * 1024, 128))
+ return TEST_ERROR;
+ if (get_pattern(string, argc, argv, 1, &pattern, 0xFFFFFFFF))
+ return TEST_ERROR;
+ if (get_addr(string, argc, argv, 2, &addr))
+ return TEST_ERROR;
+
+ printf("running noise burst for 0x%x at 0x%x + 0x%x",
+ pattern, addr, bufsize);
+
+ offset = addr;
+ remaining = bufsize;
+ size = CHUNK_SIZE;
+ while (remaining) {
+ if (remaining < size)
+ size = remaining;
+ do_noise_burst(offset, pattern, size);
+ remaining -= size;
+ offset += size;
+ if (progress(offset)) {
+ res = TEST_FAILED;
+ goto end;
+ }
+ }
+ puts("\ncheck buffer");
+ for (i = 0; i < bufsize;) {
+ if (check_addr(addr + i, pattern))
+ res = TEST_FAILED;
+ i += 4;
+ if (check_addr(addr + i, ~pattern))
+ res = TEST_FAILED;
+ i += 4;
+ if (progress(i)) {
+ res = TEST_FAILED;
+ goto end;
+ }
+ }
+end:
+ puts("\n");
+ return res;
+}
+
+/**********************************************************************
+ *
+ * Function: pattern test
+ *
+ * Description: optimized loop for read/write pattern (array of 8 u32)
+ *
+ **********************************************************************/
+#define PATTERN_SIZE 8
+static enum test_result test_loop(const u32 *pattern, u32 *address,
+ const u32 bufsize)
+{
+ int i;
+ int j;
+ enum test_result res = TEST_PASSED;
+ u32 offset, testsize, remaining;
+
+ offset = (u32)address;
+ remaining = bufsize;
+ while (remaining) {
+ testsize = bufsize > 0x1000000 ? 0x1000000 : bufsize;
+
+ __asm__("push {R0-R10}");
+ __asm__("mov r0, %0" : : "r" (pattern));
+ __asm__("mov r1, %0" : : "r" (offset));
+ __asm__("mov r2, %0" : : "r" (testsize));
+ __asm__("ldmia r0!, {R3-R10}");
+
+ __asm__("loop2:");
+ __asm__("stmia r1!, {R3-R10}");
+ __asm__("stmia r1!, {R3-R10}");
+ __asm__("stmia r1!, {R3-R10}");
+ __asm__("stmia r1!, {R3-R10}");
+ __asm__("subs r2, r2, #128");
+ __asm__("bge loop2");
+ __asm__("pop {R0-R10}");
+
+ offset += testsize;
+ remaining -= testsize;
+ if (progress((u32)offset)) {
+ res = TEST_FAILED;
+ goto end;
+ }
+ }
+
+ puts("\ncheck buffer");
+ for (i = 0; i < bufsize; i += PATTERN_SIZE * 4) {
+ for (j = 0; j < PATTERN_SIZE; j++, address++)
+ if (check_addr((u32)address, pattern[j])) {
+ res = TEST_FAILED;
+ goto end;
+ }
+ if (progress(i)) {
+ res = TEST_FAILED;
+ goto end;
+ }
+ }
+
+end:
+ puts("\n");
+ return res;
+}
+
+const u32 pattern_div1_x16[PATTERN_SIZE] = {
+ 0x0000FFFF, 0x0000FFFF, 0x0000FFFF, 0x0000FFFF,
+ 0x0000FFFF, 0x0000FFFF, 0x0000FFFF, 0x0000FFFF
+};
+
+const u32 pattern_div2_x16[PATTERN_SIZE] = {
+ 0xFFFFFFFF, 0x00000000, 0xFFFFFFFF, 0x00000000,
+ 0xFFFFFFFF, 0x00000000, 0xFFFFFFFF, 0x00000000
+};
+
+const u32 pattern_div4_x16[PATTERN_SIZE] = {
+ 0xFFFFFFFF, 0xFFFFFFFF, 0x00000000, 0x00000000,
+ 0xFFFFFFFF, 0xFFFFFFFF, 0x00000000, 0x00000000
+};
+
+const u32 pattern_div4_x32[PATTERN_SIZE] = {
+ 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000
+};
+
+const u32 pattern_mostly_zero_x16[PATTERN_SIZE] = {
+ 0x00000000, 0x00000000, 0x00000000, 0x0000FFFF,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000
+};
+
+const u32 pattern_mostly_zero_x32[PATTERN_SIZE] = {
+ 0x00000000, 0x00000000, 0x00000000, 0xFFFFFFFF,
+ 0x00000000, 0x00000000, 0x00000000, 0x00000000
+};
+
+const u32 pattern_mostly_one_x16[PATTERN_SIZE] = {
+ 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0x0000FFFF,
+ 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF
+};
+
+const u32 pattern_mostly_one_x32[PATTERN_SIZE] = {
+ 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0x00000000,
+ 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF
+};
+
+#define NB_PATTERN 5
+static enum test_result test_freq_pattern(struct stm32mp1_ddrctl *ctl,
+ struct stm32mp1_ddrphy *phy,
+ char *string, int argc, char *argv[])
+{
+ const u32 * const patterns_x16[NB_PATTERN] = {
+ pattern_div1_x16,
+ pattern_div2_x16,
+ pattern_div4_x16,
+ pattern_mostly_zero_x16,
+ pattern_mostly_one_x16,
+ };
+ const u32 * const patterns_x32[NB_PATTERN] = {
+ pattern_div2_x16,
+ pattern_div4_x16,
+ pattern_div4_x32,
+ pattern_mostly_zero_x32,
+ pattern_mostly_one_x32
+ };
+ const char *patterns_comments[NB_PATTERN] = {
+ "switching at frequency F/1",
+ "switching at frequency F/2",
+ "switching at frequency F/4",
+ "mostly zero",
+ "mostly one"
+ };
+
+ enum test_result res = TEST_PASSED, pattern_res;
+ int i, bus_width;
+ const u32 **patterns;
+ u32 bufsize, addr;
+
+ if (get_bufsize(string, argc, argv, 0, &bufsize, 4 * 1024, 128))
+ return TEST_ERROR;
+ if (get_addr(string, argc, argv, 1, &addr))
+ return TEST_ERROR;
+
+ switch (readl(&ctl->mstr) & DDRCTRL_MSTR_DATA_BUS_WIDTH_MASK) {
+ case DDRCTRL_MSTR_DATA_BUS_WIDTH_HALF:
+ case DDRCTRL_MSTR_DATA_BUS_WIDTH_QUARTER:
+ bus_width = 16;
+ break;
+ default:
+ bus_width = 32;
+ break;
+ }
+
+ printf("running test pattern at 0x%08x length 0x%x width = %d\n",
+ addr, bufsize, bus_width);
+
+ patterns =
+ (const u32 **)(bus_width == 16 ? patterns_x16 : patterns_x32);
+
+ for (i = 0; i < NB_PATTERN; i++) {
+ printf("test data pattern %s:", patterns_comments[i]);
+ pattern_res = test_loop(patterns[i], (u32 *)addr, bufsize);
+ if (pattern_res != TEST_PASSED) {
+ printf("Failed\n");
+ return pattern_res;
+ }
+ printf("Passed\n");
+ }
+
+ return res;
+}
+
+/**********************************************************************
+ *
+ * Function: pattern test with size
+ *
+ * Description: loop for write pattern
+ *
+ **********************************************************************/
+
+static enum test_result test_loop_size(const u32 *pattern, u32 size,
+ u32 *address,
+ const u32 bufsize)
+{
+ int i, j;
+ enum test_result res = TEST_PASSED;
+ u32 *p = address;
+
+ for (i = 0; i < bufsize; i += size * 4) {
+ for (j = 0; j < size ; j++, p++)
+ *p = pattern[j];
+ if (progress(i)) {
+ res = TEST_FAILED;
+ goto end;
+ }
+ }
+
+ puts("\ncheck buffer");
+ p = address;
+ for (i = 0; i < bufsize; i += size * 4) {
+ for (j = 0; j < size; j++, p++)
+ if (check_addr((u32)p, pattern[j])) {
+ res = TEST_FAILED;
+ goto end;
+ }
+ if (progress(i)) {
+ res = TEST_FAILED;
+ goto end;
+ }
+ }
+
+end:
+ puts("\n");
+ return res;
+}
+
+static enum test_result test_checkboard(struct stm32mp1_ddrctl *ctl,
+ struct stm32mp1_ddrphy *phy,
+ char *string, int argc, char *argv[])
+{
+ enum test_result res = TEST_PASSED;
+ u32 bufsize, nb_loop, loop = 0, addr;
+ int i;
+
+ u32 checkboard[2] = {0x55555555, 0xAAAAAAAA};
+
+ if (get_bufsize(string, argc, argv, 0, &bufsize, 4 * 1024, 8))
+ return TEST_ERROR;
+ if (get_nb_loop(string, argc, argv, 1, &nb_loop, 1))
+ return TEST_ERROR;
+ if (get_addr(string, argc, argv, 2, &addr))
+ return TEST_ERROR;
+
+ printf("running %d loops at 0x%08x length 0x%x\n",
+ nb_loop, addr, bufsize);
+ while (1) {
+ for (i = 0; i < 2; i++) {
+ res = test_loop_size(checkboard, 2, (u32 *)addr,
+ bufsize);
+ if (res)
+ return res;
+ checkboard[0] = ~checkboard[0];
+ checkboard[1] = ~checkboard[1];
+ }
+ if (test_loop_end(&loop, nb_loop, 1))
+ break;
+ }
+ sprintf(string, "no error for %d loops at 0x%08x length 0x%x",
+ loop, addr, bufsize);
+
+ return res;
+}
+
+static enum test_result test_blockseq(struct stm32mp1_ddrctl *ctl,
+ struct stm32mp1_ddrphy *phy,
+ char *string, int argc, char *argv[])
+{
+ enum test_result res = TEST_PASSED;
+ u32 bufsize, nb_loop, loop = 0, addr, value;
+ int i;
+
+ if (get_bufsize(string, argc, argv, 0, &bufsize, 4 * 1024, 4))
+ return TEST_ERROR;
+ if (get_nb_loop(string, argc, argv, 1, &nb_loop, 1))
+ return TEST_ERROR;
+ if (get_addr(string, argc, argv, 2, &addr))
+ return TEST_ERROR;
+
+ printf("running %d loops at 0x%08x length 0x%x\n",
+ nb_loop, addr, bufsize);
+ while (1) {
+ for (i = 0; i < 256; i++) {
+ value = i | i << 8 | i << 16 | i << 24;
+ printf("pattern = %08x", value);
+ res = test_loop_size(&value, 1, (u32 *)addr, bufsize);
+ if (res != TEST_PASSED)
+ return res;
+ }
+ if (test_loop_end(&loop, nb_loop, 1))
+ break;
+ }
+ sprintf(string, "no error for %d loops at 0x%08x length 0x%x",
+ loop, addr, bufsize);
+
+ return res;
+}
+
+static enum test_result test_walkbit0(struct stm32mp1_ddrctl *ctl,
+ struct stm32mp1_ddrphy *phy,
+ char *string, int argc, char *argv[])
+{
+ enum test_result res = TEST_PASSED;
+ u32 bufsize, nb_loop, loop = 0, addr, value;
+ int i;
+
+ if (get_bufsize(string, argc, argv, 0, &bufsize, 4 * 1024, 4))
+ return TEST_ERROR;
+ if (get_nb_loop(string, argc, argv, 1, &nb_loop, 1))
+ return TEST_ERROR;
+ if (get_addr(string, argc, argv, 2, &addr))
+ return TEST_ERROR;
+
+ printf("running %d loops at 0x%08x length 0x%x\n",
+ nb_loop, addr, bufsize);
+ while (1) {
+ for (i = 0; i < 64; i++) {
+ if (i < 32)
+ value = 1 << i;
+ else
+ value = 1 << (63 - i);
+
+ printf("pattern = %08x", value);
+ res = test_loop_size(&value, 1, (u32 *)addr, bufsize);
+ if (res != TEST_PASSED)
+ return res;
+ }
+ if (test_loop_end(&loop, nb_loop, 1))
+ break;
+ }
+ sprintf(string, "no error for %d loops at 0x%08x length 0x%x",
+ loop, addr, bufsize);
+
+ return res;
+}
+
+static enum test_result test_walkbit1(struct stm32mp1_ddrctl *ctl,
+ struct stm32mp1_ddrphy *phy,
+ char *string, int argc, char *argv[])
+{
+ enum test_result res = TEST_PASSED;
+ u32 bufsize, nb_loop, loop = 0, addr, value;
+ int i;
+
+ if (get_bufsize(string, argc, argv, 0, &bufsize, 4 * 1024, 4))
+ return TEST_ERROR;
+ if (get_nb_loop(string, argc, argv, 1, &nb_loop, 1))
+ return TEST_ERROR;
+ if (get_addr(string, argc, argv, 2, &addr))
+ return TEST_ERROR;
+
+ printf("running %d loops at 0x%08x length 0x%x\n",
+ nb_loop, addr, bufsize);
+ while (1) {
+ for (i = 0; i < 64; i++) {
+ if (i < 32)
+ value = ~(1 << i);
+ else
+ value = ~(1 << (63 - i));
+
+ printf("pattern = %08x", value);
+ res = test_loop_size(&value, 1, (u32 *)addr, bufsize);
+ if (res != TEST_PASSED)
+ return res;
+ }
+ if (test_loop_end(&loop, nb_loop, 1))
+ break;
+ }
+ sprintf(string, "no error for %d loops at 0x%08x length 0x%x",
+ loop, addr, bufsize);
+
+ return res;
+}
+
+/*
+ * try to catch bad bits which are dependent on the current values of
+ * surrounding bits in either the same word32
+ */
+static enum test_result test_bitspread(struct stm32mp1_ddrctl *ctl,
+ struct stm32mp1_ddrphy *phy,
+ char *string, int argc, char *argv[])
+{
+ enum test_result res = TEST_PASSED;
+ u32 bufsize, nb_loop, loop = 0, addr, bitspread[4];
+ int i, j;
+
+ if (get_bufsize(string, argc, argv, 0, &bufsize, 4 * 1024, 32))
+ return TEST_ERROR;
+ if (get_nb_loop(string, argc, argv, 1, &nb_loop, 1))
+ return TEST_ERROR;
+ if (get_addr(string, argc, argv, 2, &addr))
+ return TEST_ERROR;
+
+ printf("running %d loops at 0x%08x length 0x%x\n",
+ nb_loop, addr, bufsize);
+ while (1) {
+ for (i = 1; i < 32; i++) {
+ for (j = 0; j < i; j++) {
+ if (i < 32)
+ bitspread[0] = (1 << i) | (1 << j);
+ else
+ bitspread[0] = (1 << (63 - i)) |
+ (1 << (63 - j));
+ bitspread[1] = bitspread[0];
+ bitspread[2] = ~bitspread[0];
+ bitspread[3] = ~bitspread[0];
+ printf("pattern = %08x", bitspread[0]);
+
+ res = test_loop_size(bitspread, 4, (u32 *)addr,
+ bufsize);
+ if (res != TEST_PASSED)
+ return res;
+ }
+ }
+ if (test_loop_end(&loop, nb_loop, 1))
+ break;
+ }
+ sprintf(string, "no error for %d loops at 0x%08x length 0x%x",
+ loop, addr, bufsize);
+
+ return res;
+}
+
+static enum test_result test_bitflip(struct stm32mp1_ddrctl *ctl,
+ struct stm32mp1_ddrphy *phy,
+ char *string, int argc, char *argv[])
+{
+ enum test_result res = TEST_PASSED;
+ u32 bufsize, nb_loop, loop = 0, addr;
+ int i;
+
+ u32 bitflip[4];
+
+ if (get_bufsize(string, argc, argv, 0, &bufsize, 4 * 1024, 32))
+ return TEST_ERROR;
+ if (get_nb_loop(string, argc, argv, 1, &nb_loop, 1))
+ return TEST_ERROR;
+ if (get_addr(string, argc, argv, 2, &addr))
+ return TEST_ERROR;
+
+ printf("running %d loops at 0x%08x length 0x%x\n",
+ nb_loop, addr, bufsize);
+ while (1) {
+ for (i = 0; i < 32; i++) {
+ bitflip[0] = 1 << i;
+ bitflip[1] = bitflip[0];
+ bitflip[2] = ~bitflip[0];
+ bitflip[3] = bitflip[2];
+ printf("pattern = %08x", bitflip[0]);
+
+ res = test_loop_size(bitflip, 4, (u32 *)addr, bufsize);
+ if (res != TEST_PASSED)
+ return res;
+ }
+ if (test_loop_end(&loop, nb_loop, 1))
+ break;
+ }
+ sprintf(string, "no error for %d loops at 0x%08x length 0x%x",
+ loop, addr, bufsize);
+
+ return res;
+}
+
+/**********************************************************************
+ *
+ * Function: infinite read access to DDR
+ *
+ * Description: continuous read the same pattern at the same address
+ *
+ **********************************************************************/
+static enum test_result test_read(struct stm32mp1_ddrctl *ctl,
+ struct stm32mp1_ddrphy *phy,
+ char *string, int argc, char *argv[])
+{
+ u32 *addr;
+ u32 data;
+ u32 loop = 0;
+ int i, size = 1024 * 1024;
+ bool random = false;
+
+ if (get_addr(string, argc, argv, 0, (u32 *)&addr))
+ return TEST_ERROR;
+
+ if (get_pattern(string, argc, argv, 1, &data, 0xA5A5AA55))
+ return TEST_ERROR;
+
+ if ((u32)addr == ADDR_INVALID) {
+ printf("running random\n");
+ random = true;
+ } else {
+ printf("running at 0x%08x with pattern=0x%08x\n",
+ (u32)addr, data);
+ writel(data, addr);
+ }
+
+ while (1) {
+ for (i = 0; i < size; i++) {
+ if (random)
+ addr = (u32 *)(STM32_DDR_BASE +
+ (rand() & (STM32_DDR_SIZE - 1) & ~0x3));
+ data = readl(addr);
+ }
+ if (test_loop_end(&loop, 0, 1))
+ break;
+ }
+ if (random)
+ sprintf(string, "%d loops random", loop);
+ else
+ sprintf(string, "%d loops at 0x%x: %x", loop, (u32)addr, data);
+
+ return TEST_PASSED;
+}
+
+/**********************************************************************
+ *
+ * Function: infinite write access to DDR
+ *
+ * Description: continuous write the same pattern at the same address
+ *
+ **********************************************************************/
+static enum test_result test_write(struct stm32mp1_ddrctl *ctl,
+ struct stm32mp1_ddrphy *phy,
+ char *string, int argc, char *argv[])
+{
+ u32 *addr;
+ u32 data;
+ u32 loop = 0;
+ int i, size = 1024 * 1024;
+ bool random = false;
+
+ if (get_addr(string, argc, argv, 0, (u32 *)&addr))
+ return TEST_ERROR;
+
+ if (get_pattern(string, argc, argv, 1, &data, 0xA5A5AA55))
+ return TEST_ERROR;
+
+ if ((u32)addr == ADDR_INVALID) {
+ printf("running random\n");
+ random = true;
+ } else {
+ printf("running at 0x%08x with pattern 0x%08x\n",
+ (u32)addr, data);
+ }
+
+ while (1) {
+ for (i = 0; i < size; i++) {
+ if (random) {
+ addr = (u32 *)(STM32_DDR_BASE +
+ (rand() & (STM32_DDR_SIZE - 1) & ~0x3));
+ data = rand();
+ }
+ writel(data, addr);
+ }
+ if (test_loop_end(&loop, 0, 1))
+ break;
+ }
+ if (random)
+ sprintf(string, "%d loops random", loop);
+ else
+ sprintf(string, "%d loops at 0x%x: %x", loop, (u32)addr, data);
+
+ return TEST_PASSED;
+}
+
+#define NB_TEST_INFINITE 2
+static enum test_result test_all(struct stm32mp1_ddrctl *ctl,
+ struct stm32mp1_ddrphy *phy,
+ char *string, int argc, char *argv[])
+{
+ enum test_result res = TEST_PASSED, result;
+ int i, j, nb_error = 0, len;
+ u32 loop = 0, nb_loop;
+ int argc_test;
+ char *argv_test[4];
+ char loop_string[] = "1";
+ char pattern_string[] = PATTERN_DEFAULT;
+ u32 *addr;
+
+ if (get_nb_loop(string, argc, argv, 0, &nb_loop, 1))
+ return TEST_ERROR;
+
+ if (get_addr(string, argc, argv, 2, (u32 *)&addr))
+ return TEST_ERROR;
+
+ while (!nb_error) {
+ /* execute all the test except the lasts which are infinite */
+ for (i = 1; i < test_nb - NB_TEST_INFINITE; i++) {
+ argc_test = 0;
+ j = 0;
+ len = strlen(test[i].usage);
+ if (argc > 1 && j < len &&
+ !strncmp("[size]", &test[i].usage[j], 6)) {
+ argv_test[argc_test++] = argv[1];
+ j += 7;
+ }
+ if (argc > 2) {
+ if (j < len &&
+ !strncmp("[loop]", &test[i].usage[j], 6)) {
+ argv_test[argc_test++] = loop_string;
+ j += 7;
+ }
+ if (j < len &&
+ !strncmp("[pattern]", &test[i].usage[j],
+ 9)) {
+ argv_test[argc_test++] = pattern_string;
+ j += 10;
+ }
+ if (j < len &&
+ !strncmp("[addr]", &test[i].usage[j], 6)) {
+ argv_test[argc_test++] = argv[2];
+ j += 7;
+ }
+ }
+ printf("execute %d:%s\n", (int)i, test[i].name);
+ result = test[i].fct(ctl, phy, string,
+ argc_test, argv_test);
+ printf("result %d:%s = ", (int)i, test[i].name);
+ if (result != TEST_PASSED) {
+ nb_error++;
+ res = TEST_FAILED;
+ puts("Failed");
+ } else {
+ puts("Passed");
+ }
+ puts("\n\n");
+ }
+ printf("loop %d: %d/%d test failed\n\n\n",
+ loop + 1, nb_error, test_nb - NB_TEST_INFINITE);
+ if (test_loop_end(&loop, nb_loop, 1))
+ break;
+ }
+ if (res != TEST_PASSED) {
+ sprintf(string, "loop %d: %d/%d test failed", loop, nb_error,
+ test_nb - NB_TEST_INFINITE);
+ } else {
+ sprintf(string, "loop %d: %d tests passed", loop,
+ test_nb - NB_TEST_INFINITE);
+ }
+ return res;
+}
+
+/****************************************************************
+ * TEST Description
+ ****************************************************************/
+
+const struct test_desc test[] = {
+ {test_all, "All", "[loop] [size] [addr]", "Execute all tests", 3 },
+ {test_databus, "Simple DataBus", "[addr]",
+ "Verifies each data line by walking 1 on fixed address",
+ 1
+ },
+ {databuswalk0, "DataBusWalking0", "[loop] [addr]",
+ "Verifies each data bus signal can be driven low (32 word burst)",
+ 2
+ },
+ {databuswalk1, "DataBusWalking1", "[loop] [addr]",
+ "Verifies each data bus signal can be driven high (32 word burst)",
+ 2
+ },
+ {test_addressbus, "AddressBus", "[size] [addr]",
+ "Verifies each relevant bits of the address and checking for aliasing",
+ 2
+ },
+ {test_memdevice, "MemDevice", "[size] [addr]",
+ "Test the integrity of a physical memory (test every storage bit in the region)",
+ 2
+ },
+ {test_sso, "SimultaneousSwitchingOutput", "[size] [addr] ",
+ "Stress the data bus over an address range",
+ 2
+ },
+ {test_noise, "Noise", "[pattern] [addr]",
+ "Verifies r/w while forcing switching of all data bus lines.",
+ 3
+ },
+ {test_noise_burst, "NoiseBurst", "[size] [pattern] [addr]",
+ "burst transfers while forcing switching of the data bus lines",
+ 3
+ },
+ {test_random, "Random", "[size] [loop] [addr]",
+ "Verifies r/w and memcopy(burst for pseudo random value.",
+ 3
+ },
+ {test_freq_pattern, "FrequencySelectivePattern", "[size] [addr]",
+ "write & test patterns: Mostly Zero, Mostly One and F/n",
+ 2
+ },
+ {test_blockseq, "BlockSequential", "[size] [loop] [addr]",
+ "test incremental pattern",
+ 3
+ },
+ {test_checkboard, "Checkerboard", "[size] [loop] [addr]",
+ "test checker pattern",
+ 3
+ },
+ {test_bitspread, "BitSpread", "[size] [loop] [addr]",
+ "test Bit Spread pattern",
+ 3
+ },
+ {test_bitflip, "BitFlip", "[size] [loop] [addr]",
+ "test Bit Flip pattern",
+ 3
+ },
+ {test_walkbit0, "WalkingOnes", "[size] [loop] [addr]",
+ "test Walking Ones pattern",
+ 3
+ },
+ {test_walkbit1, "WalkingZeroes", "[size] [loop] [addr]",
+ "test Walking Zeroes pattern",
+ 3
+ },
+ /* need to the the 2 last one (infinite) : skipped for test all */
+ {test_read, "infinite read", "[addr] [pattern]",
+ "basic test : infinite read access (random: addr=0xFFFFFFFF)", 2},
+ {test_write, "infinite write", "[addr] [pattern]",
+ "basic test : infinite write access (random: addr=0xFFFFFFFF)", 2},
+};
+
+const int test_nb = ARRAY_SIZE(test);
diff --git a/roms/u-boot/drivers/ram/stm32mp1/stm32mp1_tests.h b/roms/u-boot/drivers/ram/stm32mp1/stm32mp1_tests.h
new file mode 100644
index 000000000..55f5d6d93
--- /dev/null
+++ b/roms/u-boot/drivers/ram/stm32mp1/stm32mp1_tests.h
@@ -0,0 +1,34 @@
+/* SPDX-License-Identifier: GPL-2.0+ OR BSD-3-Clause */
+/*
+ * Copyright (C) 2019, STMicroelectronics - All Rights Reserved
+ */
+
+#ifndef _RAM_STM32MP1_TESTS_H_
+#define _RAM_STM32MP1_TESTS_H_
+
+#include "stm32mp1_ddr_regs.h"
+
+enum test_result {
+ TEST_PASSED,
+ TEST_FAILED,
+ TEST_ERROR
+};
+
+struct test_desc {
+ enum test_result (*fct)(struct stm32mp1_ddrctl *ctl,
+ struct stm32mp1_ddrphy *phy,
+ char *string,
+ int argc, char *argv[]);
+ const char *name;
+ const char *usage;
+ const char *help;
+ u8 max_args;
+};
+
+extern const struct test_desc test[];
+extern const int test_nb;
+
+extern const struct test_desc tuning[];
+extern const int tuning_nb;
+
+#endif
diff --git a/roms/u-boot/drivers/ram/stm32mp1/stm32mp1_tuning.c b/roms/u-boot/drivers/ram/stm32mp1/stm32mp1_tuning.c
new file mode 100644
index 000000000..c8cd7c3ce
--- /dev/null
+++ b/roms/u-boot/drivers/ram/stm32mp1/stm32mp1_tuning.c
@@ -0,0 +1,1540 @@
+// 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 <console.h>
+#include <clk.h>
+#include <log.h>
+#include <ram.h>
+#include <rand.h>
+#include <reset.h>
+#include <asm/io.h>
+#include <linux/bitops.h>
+#include <linux/delay.h>
+#include <linux/iopoll.h>
+
+#include "stm32mp1_ddr_regs.h"
+#include "stm32mp1_ddr.h"
+#include "stm32mp1_tests.h"
+
+#define MAX_DQS_PHASE_IDX _144deg
+#define MAX_DQS_UNIT_IDX 7
+#define MAX_GSL_IDX 5
+#define MAX_GPS_IDX 3
+
+/* Number of bytes used in this SW. ( min 1--> max 4). */
+#define NUM_BYTES 4
+
+enum dqs_phase_enum {
+ _36deg = 0,
+ _54deg = 1,
+ _72deg = 2,
+ _90deg = 3,
+ _108deg = 4,
+ _126deg = 5,
+ _144deg = 6
+};
+
+/* BIST Result struct */
+struct BIST_result {
+ /* Overall test result:
+ * 0 Fail (any bit failed) ,
+ * 1 Success (All bits success)
+ */
+ bool test_result;
+ /* 1: true, all fail / 0: False, not all bits fail */
+ bool all_bits_fail;
+ bool bit_i_test_result[8]; /* 0 fail / 1 success */
+};
+
+/* a struct that defines tuning parameters of a byte. */
+struct tuning_position {
+ u8 phase; /* DQS phase */
+ u8 unit; /* DQS unit delay */
+ u32 bits_delay; /* Bits deskew in this byte */
+};
+
+/* 36deg, 54deg, 72deg, 90deg, 108deg, 126deg, 144deg */
+const u8 dx_dll_phase[7] = {3, 2, 1, 0, 14, 13, 12};
+
+static u8 BIST_error_max = 1;
+static u32 BIST_seed = 0x1234ABCD;
+
+static u8 get_nb_bytes(struct stm32mp1_ddrctl *ctl)
+{
+ u32 data_bus = readl(&ctl->mstr) & DDRCTRL_MSTR_DATA_BUS_WIDTH_MASK;
+ u8 nb_bytes = NUM_BYTES;
+
+ switch (data_bus) {
+ case DDRCTRL_MSTR_DATA_BUS_WIDTH_HALF:
+ nb_bytes /= 2;
+ break;
+ case DDRCTRL_MSTR_DATA_BUS_WIDTH_QUARTER:
+ nb_bytes /= 4;
+ break;
+ default:
+ break;
+ }
+
+ return nb_bytes;
+}
+
+static u8 get_nb_bank(struct stm32mp1_ddrctl *ctl)
+{
+ /* Count bank address bits */
+ u8 bits = 0;
+ u32 reg, val;
+
+ reg = readl(&ctl->addrmap1);
+ /* addrmap1.addrmap_bank_b1 */
+ val = (reg & GENMASK(5, 0)) >> 0;
+ if (val <= 31)
+ bits++;
+ /* addrmap1.addrmap_bank_b2 */
+ val = (reg & GENMASK(13, 8)) >> 8;
+ if (val <= 31)
+ bits++;
+ /* addrmap1.addrmap_bank_b3 */
+ val = (reg & GENMASK(21, 16)) >> 16;
+ if (val <= 31)
+ bits++;
+
+ return bits;
+}
+
+static u8 get_nb_col(struct stm32mp1_ddrctl *ctl)
+{
+ u8 bits;
+ u32 reg, val;
+
+ /* Count column address bits, start at 2 for b0 and b1 (fixed) */
+ bits = 2;
+
+ reg = readl(&ctl->addrmap2);
+ /* addrmap2.addrmap_col_b2 */
+ val = (reg & GENMASK(3, 0)) >> 0;
+ if (val <= 7)
+ bits++;
+ /* addrmap2.addrmap_col_b3 */
+ val = (reg & GENMASK(11, 8)) >> 8;
+ if (val <= 7)
+ bits++;
+ /* addrmap2.addrmap_col_b4 */
+ val = (reg & GENMASK(19, 16)) >> 16;
+ if (val <= 7)
+ bits++;
+ /* addrmap2.addrmap_col_b5 */
+ val = (reg & GENMASK(27, 24)) >> 24;
+ if (val <= 7)
+ bits++;
+
+ reg = readl(&ctl->addrmap3);
+ /* addrmap3.addrmap_col_b6 */
+ val = (reg & GENMASK(3, 0)) >> 0;
+ if (val <= 7)
+ bits++;
+ /* addrmap3.addrmap_col_b7 */
+ val = (reg & GENMASK(11, 8)) >> 8;
+ if (val <= 7)
+ bits++;
+ /* addrmap3.addrmap_col_b8 */
+ val = (reg & GENMASK(19, 16)) >> 16;
+ if (val <= 7)
+ bits++;
+ /* addrmap3.addrmap_col_b9 */
+ val = (reg & GENMASK(27, 24)) >> 24;
+ if (val <= 7)
+ bits++;
+
+ reg = readl(&ctl->addrmap4);
+ /* addrmap4.addrmap_col_b10 */
+ val = (reg & GENMASK(3, 0)) >> 0;
+ if (val <= 7)
+ bits++;
+ /* addrmap4.addrmap_col_b11 */
+ val = (reg & GENMASK(11, 8)) >> 8;
+ if (val <= 7)
+ bits++;
+
+ return bits;
+}
+
+static u8 get_nb_row(struct stm32mp1_ddrctl *ctl)
+{
+ /* Count row address bits */
+ u8 bits = 0;
+ u32 reg, val;
+
+ reg = readl(&ctl->addrmap5);
+ /* addrmap5.addrmap_row_b0 */
+ val = (reg & GENMASK(3, 0)) >> 0;
+ if (val <= 11)
+ bits++;
+ /* addrmap5.addrmap_row_b1 */
+ val = (reg & GENMASK(11, 8)) >> 8;
+ if (val <= 11)
+ bits++;
+ /* addrmap5.addrmap_row_b2_10 */
+ val = (reg & GENMASK(19, 16)) >> 16;
+ if (val <= 11)
+ bits += 9;
+ else
+ printf("warning: addrmap5.addrmap_row_b2_10 not supported\n");
+ /* addrmap5.addrmap_row_b11 */
+ val = (reg & GENMASK(27, 24)) >> 24;
+ if (val <= 11)
+ bits++;
+
+ reg = readl(&ctl->addrmap6);
+ /* addrmap6.addrmap_row_b12 */
+ val = (reg & GENMASK(3, 0)) >> 0;
+ if (val <= 7)
+ bits++;
+ /* addrmap6.addrmap_row_b13 */
+ val = (reg & GENMASK(11, 8)) >> 8;
+ if (val <= 7)
+ bits++;
+ /* addrmap6.addrmap_row_b14 */
+ val = (reg & GENMASK(19, 16)) >> 16;
+ if (val <= 7)
+ bits++;
+ /* addrmap6.addrmap_row_b15 */
+ val = (reg & GENMASK(27, 24)) >> 24;
+ if (val <= 7)
+ bits++;
+
+ return bits;
+}
+
+static void itm_soft_reset(struct stm32mp1_ddrphy *phy)
+{
+ stm32mp1_ddrphy_init(phy, DDRPHYC_PIR_ITMSRST);
+}
+
+/* Read DQ unit delay register and provides the retrieved value for DQS
+ * We are assuming that we have the same delay when clocking
+ * by DQS and when clocking by DQSN
+ */
+static u8 DQ_unit_index(struct stm32mp1_ddrphy *phy, u8 byte, u8 bit)
+{
+ u32 index;
+ u32 addr = DXNDQTR(phy, byte);
+
+ /* We are assuming that we have the same delay when clocking by DQS
+ * and when clocking by DQSN : use only the low bits
+ */
+ index = (readl(addr) >> DDRPHYC_DXNDQTR_DQDLY_SHIFT(bit))
+ & DDRPHYC_DXNDQTR_DQDLY_LOW_MASK;
+
+ log_debug("[%x]: %x => DQ unit index = %x\n", addr, readl(addr), index);
+
+ return index;
+}
+
+/* Sets the DQS phase delay for a byte lane.
+ *phase delay is specified by giving the index of the desired delay
+ * in the dx_dll_phase array.
+ */
+static void DQS_phase_delay(struct stm32mp1_ddrphy *phy, u8 byte, u8 phase_idx)
+{
+ u8 sdphase_val = 0;
+
+ /* Write DXNDLLCR.SDPHASE = dx_dll_phase(phase_index); */
+ sdphase_val = dx_dll_phase[phase_idx];
+ clrsetbits_le32(DXNDLLCR(phy, byte),
+ DDRPHYC_DXNDLLCR_SDPHASE_MASK,
+ sdphase_val << DDRPHYC_DXNDLLCR_SDPHASE_SHIFT);
+}
+
+/* Sets the DQS unit delay for a byte lane.
+ * unit delay is specified by giving the index of the desired delay
+ * for dgsdly and dqsndly (same value).
+ */
+static void DQS_unit_delay(struct stm32mp1_ddrphy *phy,
+ u8 byte, u8 unit_dly_idx)
+{
+ /* Write the same value in DXNDQSTR.DQSDLY and DXNDQSTR.DQSNDLY */
+ clrsetbits_le32(DXNDQSTR(phy, byte),
+ DDRPHYC_DXNDQSTR_DQSDLY_MASK |
+ DDRPHYC_DXNDQSTR_DQSNDLY_MASK,
+ (unit_dly_idx << DDRPHYC_DXNDQSTR_DQSDLY_SHIFT) |
+ (unit_dly_idx << DDRPHYC_DXNDQSTR_DQSNDLY_SHIFT));
+
+ /* After changing this value, an ITM soft reset (PIR.ITMSRST=1,
+ * plus PIR.INIT=1) must be issued.
+ */
+ stm32mp1_ddrphy_init(phy, DDRPHYC_PIR_ITMSRST);
+}
+
+/* Sets the DQ unit delay for a bit line in particular byte lane.
+ * unit delay is specified by giving the desired delay
+ */
+static void set_DQ_unit_delay(struct stm32mp1_ddrphy *phy,
+ u8 byte, u8 bit,
+ u8 dq_delay_index)
+{
+ u8 dq_bit_delay_val = dq_delay_index | (dq_delay_index << 2);
+
+ /* same value on delay for clock DQ an DQS_b */
+ clrsetbits_le32(DXNDQTR(phy, byte),
+ DDRPHYC_DXNDQTR_DQDLY_MASK
+ << DDRPHYC_DXNDQTR_DQDLY_SHIFT(bit),
+ dq_bit_delay_val << DDRPHYC_DXNDQTR_DQDLY_SHIFT(bit));
+}
+
+static void set_r0dgsl_delay(struct stm32mp1_ddrphy *phy,
+ u8 byte, u8 r0dgsl_idx)
+{
+ clrsetbits_le32(DXNDQSTR(phy, byte),
+ DDRPHYC_DXNDQSTR_R0DGSL_MASK,
+ r0dgsl_idx << DDRPHYC_DXNDQSTR_R0DGSL_SHIFT);
+}
+
+static void set_r0dgps_delay(struct stm32mp1_ddrphy *phy,
+ u8 byte, u8 r0dgps_idx)
+{
+ clrsetbits_le32(DXNDQSTR(phy, byte),
+ DDRPHYC_DXNDQSTR_R0DGPS_MASK,
+ r0dgps_idx << DDRPHYC_DXNDQSTR_R0DGPS_SHIFT);
+}
+
+/* Basic BIST configuration for data lane tests. */
+static void config_BIST(struct stm32mp1_ddrctl *ctl,
+ struct stm32mp1_ddrphy *phy)
+{
+ u8 nb_bank = get_nb_bank(ctl);
+ u8 nb_row = get_nb_row(ctl);
+ u8 nb_col = get_nb_col(ctl);
+
+ /* Selects the SDRAM bank address to be used during BIST. */
+ u32 bbank = 0;
+ /* Selects the SDRAM row address to be used during BIST. */
+ u32 brow = 0;
+ /* Selects the SDRAM column address to be used during BIST. */
+ u32 bcol = 0;
+ /* Selects the value by which the SDRAM address is incremented
+ * for each write/read access.
+ */
+ u32 bainc = 0x00000008;
+ /* Specifies the maximum SDRAM rank to be used during BIST.
+ * The default value is set to maximum ranks minus 1.
+ * must be 0 with single rank
+ */
+ u32 bmrank = 0;
+ /* Selects the SDRAM rank to be used during BIST.
+ * must be 0 with single rank
+ */
+ u32 brank = 0;
+
+ /* Specifies the maximum SDRAM bank address to be used during
+ * BIST before the address & increments to the next rank.
+ */
+ u32 bmbank = (1 << nb_bank) - 1;
+ /* Specifies the maximum SDRAM row address to be used during
+ * BIST before the address & increments to the next bank.
+ */
+ u32 bmrow = (1 << nb_row) - 1;
+ /* Specifies the maximum SDRAM column address to be used during
+ * BIST before the address & increments to the next row.
+ */
+ u32 bmcol = (1 << nb_col) - 1;
+
+ u32 bmode_conf = 0x00000001; /* DRam mode */
+ u32 bdxen_conf = 0x00000001; /* BIST on Data byte */
+ u32 bdpat_conf = 0x00000002; /* Select LFSR pattern */
+
+ /*Setup BIST for DRAM mode, and LFSR-random data pattern.*/
+ /*Write BISTRR.BMODE = 1?b1;*/
+ /*Write BISTRR.BDXEN = 1?b1;*/
+ /*Write BISTRR.BDPAT = 2?b10;*/
+
+ /* reset BIST */
+ writel(0x3, &phy->bistrr);
+
+ writel((bmode_conf << 3) | (bdxen_conf << 14) | (bdpat_conf << 17),
+ &phy->bistrr);
+
+ /*Setup BIST Word Count*/
+ /*Write BISTWCR.BWCNT = 16?b0008;*/
+ writel(0x00000200, &phy->bistwcr); /* A multiple of BL/2 */
+
+ writel(bcol | (brow << 12) | (bbank << 28), &phy->bistar0);
+ writel(brank | (bmrank << 2) | (bainc << 4), &phy->bistar1);
+ writel(bmcol | (bmrow << 12) | (bmbank << 28), &phy->bistar2);
+}
+
+/* Select the Byte lane to be tested by BIST. */
+static void BIST_datx8_sel(struct stm32mp1_ddrphy *phy, u8 datx8)
+{
+ clrsetbits_le32(&phy->bistrr,
+ DDRPHYC_BISTRR_BDXSEL_MASK,
+ datx8 << DDRPHYC_BISTRR_BDXSEL_SHIFT);
+
+ /*(For example, selecting Byte Lane 3, BISTRR.BDXSEL = 4?b0011)*/
+ /* Write BISTRR.BDXSEL = datx8; */
+}
+
+/* Perform BIST Write_Read test on a byte lane and return test result. */
+static void BIST_test(struct stm32mp1_ddrphy *phy, u8 byte,
+ struct BIST_result *bist)
+{
+ bool result = true; /* BIST_SUCCESS */
+ u32 cnt = 0;
+ u32 error = 0;
+ u32 val;
+ int ret;
+
+ bist->test_result = true;
+
+run:
+ itm_soft_reset(phy);
+
+ /*Perform BIST Reset*/
+ /* Write BISTRR.BINST = 3?b011; */
+ clrsetbits_le32(&phy->bistrr,
+ 0x00000007,
+ 0x00000003);
+
+ /*Re-seed LFSR*/
+ /* Write BISTLSR.SEED = 32'h1234ABCD; */
+ if (BIST_seed)
+ writel(BIST_seed, &phy->bistlsr);
+ else
+ writel(rand(), &phy->bistlsr);
+
+ /* some delay to reset BIST */
+ udelay(10);
+
+ /*Perform BIST Run*/
+ clrsetbits_le32(&phy->bistrr,
+ 0x00000007,
+ 0x00000001);
+ /* Write BISTRR.BINST = 3?b001; */
+
+ /* poll on BISTGSR.BDONE and wait max 1000 us */
+ ret = readl_poll_timeout(&phy->bistgsr, val,
+ val & DDRPHYC_BISTGSR_BDDONE, 1000);
+
+ if (ret < 0) {
+ printf("warning: BIST timeout\n");
+ result = false; /* BIST_FAIL; */
+ /*Perform BIST Stop */
+ clrsetbits_le32(&phy->bistrr, 0x00000007, 0x00000002);
+ } else {
+ /*Check if received correct number of words*/
+ /* if (Read BISTWCSR.DXWCNT = Read BISTWCR.BWCNT) */
+ if (((readl(&phy->bistwcsr)) >> DDRPHYC_BISTWCSR_DXWCNT_SHIFT)
+ == readl(&phy->bistwcr)) {
+ /*Determine if there is a data comparison error*/
+ /* if (Read BISTGSR.BDXERR = 1?b0) */
+ if (readl(&phy->bistgsr) & DDRPHYC_BISTGSR_BDXERR)
+ result = false; /* BIST_FAIL; */
+ else
+ result = true; /* BIST_SUCCESS; */
+ } else {
+ result = false; /* BIST_FAIL; */
+ }
+ }
+
+ /* loop while success */
+ cnt++;
+ if (result && cnt != 1000)
+ goto run;
+
+ if (!result)
+ error++;
+
+ if (error < BIST_error_max) {
+ if (cnt != 1000)
+ goto run;
+ bist->test_result = true;
+ } else {
+ bist->test_result = false;
+ }
+}
+
+/* After running the deskew algo, this function applies the new DQ delays
+ * by reading them from the array "deskew_delay"and writing in PHY registers.
+ * The bits that are not deskewed parfectly (too much skew on them,
+ * or data eye very wide) are marked in the array deskew_non_converge.
+ */
+static void apply_deskew_results(struct stm32mp1_ddrphy *phy, u8 byte,
+ u8 deskew_delay[NUM_BYTES][8],
+ u8 deskew_non_converge[NUM_BYTES][8])
+{
+ u8 bit_i;
+ u8 index;
+
+ for (bit_i = 0; bit_i < 8; bit_i++) {
+ set_DQ_unit_delay(phy, byte, bit_i, deskew_delay[byte][bit_i]);
+ index = DQ_unit_index(phy, byte, bit_i);
+ log_debug("Byte %d ; bit %d : The new DQ delay (%d) index=%d [delta=%d, 3 is the default]",
+ byte, bit_i, deskew_delay[byte][bit_i],
+ index, index - 3);
+ printf("Byte %d, bit %d, DQ delay = %d",
+ byte, bit_i, deskew_delay[byte][bit_i]);
+ if (deskew_non_converge[byte][bit_i] == 1)
+ log_debug(" - not converged : still more skew");
+ printf("\n");
+ }
+}
+
+/* DQ Bit de-skew algorithm.
+ * Deskews data lines as much as possible.
+ * 1. Add delay to DQS line until finding the failure
+ * (normally a hold time violation)
+ * 2. Reduce DQS line by small steps until finding the very first time
+ * we go back to "Pass" condition.
+ * 3. For each DQ line, Reduce DQ delay until finding the very first failure
+ * (normally a hold time fail)
+ * 4. When all bits are at their first failure delay, we can consider them
+ * aligned.
+ * Handle conrer situation (Can't find Pass-fail, or fail-pass transitions
+ * at any step)
+ * TODO Provide a return Status. Improve doc
+ */
+static enum test_result bit_deskew(struct stm32mp1_ddrctl *ctl,
+ struct stm32mp1_ddrphy *phy, char *string)
+{
+ /* New DQ delay value (index), set during Deskew algo */
+ u8 deskew_delay[NUM_BYTES][8];
+ /*If there is still skew on a bit, mark this bit. */
+ u8 deskew_non_converge[NUM_BYTES][8];
+ struct BIST_result result;
+ s8 dqs_unit_delay_index = 0;
+ u8 datx8 = 0;
+ u8 bit_i = 0;
+ s8 phase_idx = 0;
+ s8 bit_i_delay_index = 0;
+ u8 success = 0;
+ struct tuning_position last_right_ok;
+ u8 force_stop = 0;
+ u8 fail_found;
+ u8 error = 0;
+ u8 nb_bytes = get_nb_bytes(ctl);
+ /* u8 last_pass_dqs_unit = 0; */
+
+ memset(deskew_delay, 0, sizeof(deskew_delay));
+ memset(deskew_non_converge, 0, sizeof(deskew_non_converge));
+
+ /*Disable DQS Drift Compensation*/
+ clrbits_le32(&phy->pgcr, DDRPHYC_PGCR_DFTCMP);
+ /*Disable all bytes*/
+ /* Disable automatic power down of DLL and IOs when disabling
+ * a byte (To avoid having to add programming and delay
+ * for a DLL re-lock when later re-enabling a disabled Byte Lane)
+ */
+ clrbits_le32(&phy->pgcr, DDRPHYC_PGCR_PDDISDX);
+
+ /* Disable all data bytes */
+ clrbits_le32(&phy->dx0gcr, DDRPHYC_DXNGCR_DXEN);
+ clrbits_le32(&phy->dx1gcr, DDRPHYC_DXNGCR_DXEN);
+ clrbits_le32(&phy->dx2gcr, DDRPHYC_DXNGCR_DXEN);
+ clrbits_le32(&phy->dx3gcr, DDRPHYC_DXNGCR_DXEN);
+
+ /* Config the BIST block */
+ config_BIST(ctl, phy);
+ log_debug("BIST Config done.\n");
+
+ /* Train each byte */
+ for (datx8 = 0; datx8 < nb_bytes; datx8++) {
+ if (ctrlc()) {
+ sprintf(string, "interrupted at byte %d/%d, error=%d",
+ datx8 + 1, nb_bytes, error);
+ return TEST_FAILED;
+ }
+ log_debug("\n======================\n");
+ log_debug("Start deskew byte %d .\n", datx8);
+ log_debug("======================\n");
+ /* Enable Byte (DXNGCR, bit DXEN) */
+ setbits_le32(DXNGCR(phy, datx8), DDRPHYC_DXNGCR_DXEN);
+
+ /* Select the byte lane for comparison of read data */
+ BIST_datx8_sel(phy, datx8);
+
+ /* Set all DQDLYn to maximum value. All bits within the byte
+ * will be delayed with DQSTR = 2 instead of max = 3
+ * to avoid inter bits fail influence
+ */
+ writel(0xAAAAAAAA, DXNDQTR(phy, datx8));
+
+ /* Set the DQS phase delay to 90 DEG (default).
+ * What is defined here is the index of the desired config
+ * in the PHASE array.
+ */
+ phase_idx = _90deg;
+
+ /* Set DQS unit delay to the max value. */
+ dqs_unit_delay_index = MAX_DQS_UNIT_IDX;
+ DQS_unit_delay(phy, datx8, dqs_unit_delay_index);
+ DQS_phase_delay(phy, datx8, phase_idx);
+
+ /* Issue a DLL soft reset */
+ clrbits_le32(DXNDLLCR(phy, datx8), DDRPHYC_DXNDLLCR_DLLSRST);
+ setbits_le32(DXNDLLCR(phy, datx8), DDRPHYC_DXNDLLCR_DLLSRST);
+
+ /* Test this typical init condition */
+ BIST_test(phy, datx8, &result);
+ success = result.test_result;
+
+ /* If the test pass in this typical condition,
+ * start the algo with it.
+ * Else, look for Pass init condition
+ */
+ if (!success) {
+ log_debug("Fail at init condtion. Let's look for a good init condition.\n");
+ success = 0; /* init */
+ /* Make sure we start with a PASS condition before
+ * looking for a fail condition.
+ * Find the first PASS PHASE condition
+ */
+
+ /* escape if we find a PASS */
+ log_debug("increase Phase idx\n");
+ while (!success && (phase_idx <= MAX_DQS_PHASE_IDX)) {
+ DQS_phase_delay(phy, datx8, phase_idx);
+ BIST_test(phy, datx8, &result);
+ success = result.test_result;
+ phase_idx++;
+ }
+ /* if ended with success
+ * ==>> Restore the fist success condition
+ */
+ if (success)
+ phase_idx--; /* because it ended with ++ */
+ }
+ if (ctrlc()) {
+ sprintf(string, "interrupted at byte %d/%d, error=%d",
+ datx8 + 1, nb_bytes, error);
+ return TEST_FAILED;
+ }
+ /* We couldn't find a successful condition, its seems
+ * we have hold violation, lets try reduce DQS_unit Delay
+ */
+ if (!success) {
+ /* We couldn't find a successful condition, its seems
+ * we have hold violation, lets try reduce DQS_unit
+ * Delay
+ */
+ log_debug("Still fail. Try decrease DQS Unit delay\n");
+
+ phase_idx = 0;
+ dqs_unit_delay_index = 0;
+ DQS_phase_delay(phy, datx8, phase_idx);
+
+ /* escape if we find a PASS */
+ while (!success &&
+ (dqs_unit_delay_index <=
+ MAX_DQS_UNIT_IDX)) {
+ DQS_unit_delay(phy, datx8,
+ dqs_unit_delay_index);
+ BIST_test(phy, datx8, &result);
+ success = result.test_result;
+ dqs_unit_delay_index++;
+ }
+ if (success) {
+ /* Restore the first success condition*/
+ dqs_unit_delay_index--;
+ /* last_pass_dqs_unit = dqs_unit_delay_index;*/
+ DQS_unit_delay(phy, datx8,
+ dqs_unit_delay_index);
+ } else {
+ /* No need to continue,
+ * there is no pass region.
+ */
+ force_stop = 1;
+ }
+ }
+
+ /* There is an initial PASS condition
+ * Look for the first failing condition by PHASE stepping.
+ * This part of the algo can finish without converging.
+ */
+ if (force_stop) {
+ printf("Result: Failed ");
+ printf("[Cannot Deskew lines, ");
+ printf("there is no PASS region]\n");
+ error++;
+ continue;
+ }
+ if (ctrlc()) {
+ sprintf(string, "interrupted at byte %d/%d, error=%d",
+ datx8 + 1, nb_bytes, error);
+ return TEST_FAILED;
+ }
+
+ log_debug("there is a pass region for phase idx %d\n",
+ phase_idx);
+ log_debug("Step1: Find the first failing condition\n");
+ /* Look for the first failing condition by PHASE stepping.
+ * This part of the algo can finish without converging.
+ */
+
+ /* escape if we find a fail (hold time violation)
+ * condition at any bit or if out of delay range.
+ */
+ while (success && (phase_idx <= MAX_DQS_PHASE_IDX)) {
+ DQS_phase_delay(phy, datx8, phase_idx);
+ BIST_test(phy, datx8, &result);
+ success = result.test_result;
+ phase_idx++;
+ }
+ if (ctrlc()) {
+ sprintf(string, "interrupted at byte %d/%d, error=%d",
+ datx8 + 1, nb_bytes, error);
+ return TEST_FAILED;
+ }
+
+ /* if the loop ended with a failing condition at any bit,
+ * lets look for the first previous success condition by unit
+ * stepping (minimal delay)
+ */
+ if (!success) {
+ log_debug("Fail region (PHASE) found phase idx %d\n",
+ phase_idx);
+ log_debug("Let's look for first success by DQS Unit steps\n");
+ /* This part, the algo always converge */
+ phase_idx--;
+
+ /* escape if we find a success condition
+ * or if out of delay range.
+ */
+ while (!success && dqs_unit_delay_index >= 0) {
+ DQS_unit_delay(phy, datx8,
+ dqs_unit_delay_index);
+ BIST_test(phy, datx8, &result);
+ success = result.test_result;
+ dqs_unit_delay_index--;
+ }
+ /* if the loop ended with a success condition,
+ * the last delay Right OK (before hold violation)
+ * condition is then defined as following:
+ */
+ if (success) {
+ /* Hold the dely parameters of the the last
+ * delay Right OK condition.
+ * -1 to get back to current condition
+ */
+ last_right_ok.phase = phase_idx;
+ /*+1 to get back to current condition */
+ last_right_ok.unit = dqs_unit_delay_index + 1;
+ last_right_ok.bits_delay = 0xFFFFFFFF;
+ log_debug("Found %d\n", dqs_unit_delay_index);
+ } else {
+ /* the last OK condition is then with the
+ * previous phase_idx.
+ * -2 instead of -1 because at the last
+ * iteration of the while(),
+ * we incremented phase_idx
+ */
+ last_right_ok.phase = phase_idx - 1;
+ /* Nominal+1. Because we want the previous
+ * delay after reducing the phase delay.
+ */
+ last_right_ok.unit = 1;
+ last_right_ok.bits_delay = 0xFFFFFFFF;
+ log_debug("Not Found : try previous phase %d\n",
+ phase_idx - 1);
+
+ DQS_phase_delay(phy, datx8, phase_idx - 1);
+ dqs_unit_delay_index = 0;
+ success = true;
+ while (success &&
+ (dqs_unit_delay_index <
+ MAX_DQS_UNIT_IDX)) {
+ DQS_unit_delay(phy, datx8,
+ dqs_unit_delay_index);
+ BIST_test(phy, datx8, &result);
+ success = result.test_result;
+ dqs_unit_delay_index++;
+ log_debug("dqs_unit_delay_index = %d, result = %d\n",
+ dqs_unit_delay_index, success);
+ }
+
+ if (!success) {
+ last_right_ok.unit =
+ dqs_unit_delay_index - 1;
+ } else {
+ last_right_ok.unit = 0;
+ log_debug("ERROR: failed region not FOUND");
+ }
+ }
+ } else {
+ /* we can't find a failing condition at all bits
+ * ==> Just hold the last test condition
+ * (the max DQS delay)
+ * which is the most likely,
+ * the closest to a hold violation
+ * If we can't find a Fail condition after
+ * the Pass region, stick at this position
+ * In order to have max chances to find a fail
+ * when reducing DQ delays.
+ */
+ last_right_ok.phase = MAX_DQS_PHASE_IDX;
+ last_right_ok.unit = MAX_DQS_UNIT_IDX;
+ last_right_ok.bits_delay = 0xFFFFFFFF;
+ log_debug("Can't find the a fail condition\n");
+ }
+
+ /* step 2:
+ * if we arrive at this stage, it means that we found the last
+ * Right OK condition (by tweeking the DQS delay). Or we simply
+ * pushed DQS delay to the max
+ * This means that by reducing the delay on some DQ bits,
+ * we should find a failing condition.
+ */
+ printf("Byte %d, DQS unit = %d, phase = %d\n",
+ datx8, last_right_ok.unit, last_right_ok.phase);
+ log_debug("Step2, unit = %d, phase = %d, bits delay=%x\n",
+ last_right_ok.unit, last_right_ok.phase,
+ last_right_ok.bits_delay);
+
+ /* Restore the last_right_ok condtion. */
+ DQS_unit_delay(phy, datx8, last_right_ok.unit);
+ DQS_phase_delay(phy, datx8, last_right_ok.phase);
+ writel(last_right_ok.bits_delay, DXNDQTR(phy, datx8));
+
+ /* train each bit
+ * reduce delay on each bit, and perform a write/read test
+ * and stop at the very first time it fails.
+ * the goal is the find the first failing condition
+ * for each bit.
+ * When we achieve this condition< for all the bits,
+ * we are sure they are aligned (+/- step resolution)
+ */
+ fail_found = 0;
+ for (bit_i = 0; bit_i < 8; bit_i++) {
+ if (ctrlc()) {
+ sprintf(string,
+ "interrupted at byte %d/%d, error=%d",
+ datx8 + 1, nb_bytes, error);
+ return error;
+ }
+ log_debug("deskewing bit %d:\n", bit_i);
+ success = 1; /* init */
+ /* Set all DQDLYn to maximum value.
+ * Only bit_i will be down-delayed
+ * ==> if we have a fail, it will be definitely
+ * from bit_i
+ */
+ writel(0xFFFFFFFF, DXNDQTR(phy, datx8));
+ /* Arriving at this stage,
+ * we have a success condition with delay = 3;
+ */
+ bit_i_delay_index = 3;
+
+ /* escape if bit delay is out of range or
+ * if a fatil occurs
+ */
+ while ((bit_i_delay_index >= 0) && success) {
+ set_DQ_unit_delay(phy, datx8,
+ bit_i,
+ bit_i_delay_index);
+ BIST_test(phy, datx8, &result);
+ success = result.test_result;
+ bit_i_delay_index--;
+ }
+
+ /* if escape with a fail condition
+ * ==> save this position for bit_i
+ */
+ if (!success) {
+ /* save the delay position.
+ * Add 1 because the while loop ended with a --,
+ * and that we need to hold the last success
+ * delay
+ */
+ deskew_delay[datx8][bit_i] =
+ bit_i_delay_index + 2;
+ if (deskew_delay[datx8][bit_i] > 3)
+ deskew_delay[datx8][bit_i] = 3;
+
+ /* A flag that states we found at least a fail
+ * at one bit.
+ */
+ fail_found = 1;
+ log_debug("Fail found on bit %d, for delay = %d => deskew[%d][%d] = %d\n",
+ bit_i, bit_i_delay_index + 1,
+ datx8, bit_i,
+ deskew_delay[datx8][bit_i]);
+ } else {
+ /* if we can find a success condition by
+ * back-delaying this bit, just set the delay
+ * to 0 (the best deskew
+ * possible) and mark the bit.
+ */
+ deskew_delay[datx8][bit_i] = 0;
+ /* set a flag that will be used later
+ * in the report.
+ */
+ deskew_non_converge[datx8][bit_i] = 1;
+ log_debug("Fail not found on bit %d => deskew[%d][%d] = %d\n",
+ bit_i, datx8, bit_i,
+ deskew_delay[datx8][bit_i]);
+ }
+ }
+ log_debug("**********byte %d tuning complete************\n",
+ datx8);
+ /* If we can't find any failure by back delaying DQ lines,
+ * hold the default values
+ */
+ if (!fail_found) {
+ for (bit_i = 0; bit_i < 8; bit_i++)
+ deskew_delay[datx8][bit_i] = 0;
+ log_debug("The Deskew algorithm can't converge, there is too much margin in your design. Good job!\n");
+ }
+
+ apply_deskew_results(phy, datx8, deskew_delay,
+ deskew_non_converge);
+ /* Restore nominal value for DQS delay */
+ DQS_phase_delay(phy, datx8, 3);
+ DQS_unit_delay(phy, datx8, 3);
+ /* disable byte after byte bits deskew */
+ clrbits_le32(DXNGCR(phy, datx8), DDRPHYC_DXNGCR_DXEN);
+ } /* end of byte deskew */
+
+ /* re-enable all data bytes */
+ setbits_le32(&phy->dx0gcr, DDRPHYC_DXNGCR_DXEN);
+ setbits_le32(&phy->dx1gcr, DDRPHYC_DXNGCR_DXEN);
+ setbits_le32(&phy->dx2gcr, DDRPHYC_DXNGCR_DXEN);
+ setbits_le32(&phy->dx3gcr, DDRPHYC_DXNGCR_DXEN);
+
+ if (error) {
+ sprintf(string, "error = %d", error);
+ return TEST_FAILED;
+ }
+
+ return TEST_PASSED;
+} /* end function */
+
+/* Trim DQS timings and set it in the centre of data eye.
+ * Look for a PPPPF region, then look for a FPPP region and finally select
+ * the mid of the FPPPPPF region
+ */
+static enum test_result eye_training(struct stm32mp1_ddrctl *ctl,
+ struct stm32mp1_ddrphy *phy, char *string)
+{
+ /*Stores the DQS trim values (PHASE index, unit index) */
+ u8 eye_training_val[NUM_BYTES][2];
+ u8 byte = 0;
+ struct BIST_result result;
+ s8 dqs_unit_delay_index = 0;
+ s8 phase_idx = 0;
+ s8 dqs_unit_delay_index_pass = 0;
+ s8 phase_idx_pass = 0;
+ u8 success = 0;
+ u8 left_phase_bound_found, right_phase_bound_found;
+ u8 left_unit_bound_found, right_unit_bound_found;
+ u8 left_bound_found, right_bound_found;
+ struct tuning_position left_bound, right_bound;
+ u8 error = 0;
+ u8 nb_bytes = get_nb_bytes(ctl);
+
+ /*Disable DQS Drift Compensation*/
+ clrbits_le32(&phy->pgcr, DDRPHYC_PGCR_DFTCMP);
+ /*Disable all bytes*/
+ /* Disable automatic power down of DLL and IOs when disabling a byte
+ * (To avoid having to add programming and delay
+ * for a DLL re-lock when later re-enabling a disabled Byte Lane)
+ */
+ clrbits_le32(&phy->pgcr, DDRPHYC_PGCR_PDDISDX);
+
+ /*Disable all data bytes */
+ clrbits_le32(&phy->dx0gcr, DDRPHYC_DXNGCR_DXEN);
+ clrbits_le32(&phy->dx1gcr, DDRPHYC_DXNGCR_DXEN);
+ clrbits_le32(&phy->dx2gcr, DDRPHYC_DXNGCR_DXEN);
+ clrbits_le32(&phy->dx3gcr, DDRPHYC_DXNGCR_DXEN);
+
+ /* Config the BIST block */
+ config_BIST(ctl, phy);
+
+ for (byte = 0; byte < nb_bytes; byte++) {
+ if (ctrlc()) {
+ sprintf(string, "interrupted at byte %d/%d, error=%d",
+ byte + 1, nb_bytes, error);
+ return TEST_FAILED;
+ }
+ right_bound.phase = 0;
+ right_bound.unit = 0;
+
+ left_bound.phase = 0;
+ left_bound.unit = 0;
+
+ left_phase_bound_found = 0;
+ right_phase_bound_found = 0;
+
+ left_unit_bound_found = 0;
+ right_unit_bound_found = 0;
+
+ left_bound_found = 0;
+ right_bound_found = 0;
+
+ /* Enable Byte (DXNGCR, bit DXEN) */
+ setbits_le32(DXNGCR(phy, byte), DDRPHYC_DXNGCR_DXEN);
+
+ /* Select the byte lane for comparison of read data */
+ BIST_datx8_sel(phy, byte);
+
+ /* Set DQS phase delay to the nominal value. */
+ phase_idx = _90deg;
+ phase_idx_pass = phase_idx;
+
+ /* Set DQS unit delay to the nominal value. */
+ dqs_unit_delay_index = 3;
+ dqs_unit_delay_index_pass = dqs_unit_delay_index;
+ success = 0;
+
+ log_debug("STEP0: Find Init delay\n");
+ /* STEP0: Find Init delay: a delay that put the system
+ * in a "Pass" condition then (TODO) update
+ * dqs_unit_delay_index_pass & phase_idx_pass
+ */
+ DQS_unit_delay(phy, byte, dqs_unit_delay_index);
+ DQS_phase_delay(phy, byte, phase_idx);
+ BIST_test(phy, byte, &result);
+ success = result.test_result;
+ /* If we have a fail in the nominal condition */
+ if (!success) {
+ /* Look at the left */
+ while (phase_idx >= 0 && !success) {
+ phase_idx--;
+ DQS_phase_delay(phy, byte, phase_idx);
+ BIST_test(phy, byte, &result);
+ success = result.test_result;
+ }
+ }
+ if (!success) {
+ /* if we can't find pass condition,
+ * then look at the right
+ */
+ phase_idx = _90deg;
+ while (phase_idx <= MAX_DQS_PHASE_IDX &&
+ !success) {
+ phase_idx++;
+ DQS_phase_delay(phy, byte,
+ phase_idx);
+ BIST_test(phy, byte, &result);
+ success = result.test_result;
+ }
+ }
+ /* save the pass condition */
+ if (success) {
+ phase_idx_pass = phase_idx;
+ } else {
+ printf("Result: Failed ");
+ printf("[Cannot DQS timings, ");
+ printf("there is no PASS region]\n");
+ error++;
+ continue;
+ }
+
+ if (ctrlc()) {
+ sprintf(string, "interrupted at byte %d/%d, error=%d",
+ byte + 1, nb_bytes, error);
+ return TEST_FAILED;
+ }
+ log_debug("STEP1: Find LEFT PHASE DQS Bound\n");
+ /* STEP1: Find LEFT PHASE DQS Bound */
+ while ((phase_idx >= 0) &&
+ (phase_idx <= MAX_DQS_PHASE_IDX) &&
+ !left_phase_bound_found) {
+ DQS_unit_delay(phy, byte,
+ dqs_unit_delay_index);
+ DQS_phase_delay(phy, byte,
+ phase_idx);
+ BIST_test(phy, byte, &result);
+ success = result.test_result;
+
+ /*TODO: Manage the case were at the beginning
+ * there is already a fail
+ */
+ if (!success) {
+ /* the last pass condition */
+ left_bound.phase = ++phase_idx;
+ left_phase_bound_found = 1;
+ } else if (success) {
+ phase_idx--;
+ }
+ }
+ if (!left_phase_bound_found) {
+ left_bound.phase = 0;
+ phase_idx = 0;
+ }
+ /* If not found, lets take 0 */
+
+ if (ctrlc()) {
+ sprintf(string, "interrupted at byte %d/%d, error=%d",
+ byte + 1, nb_bytes, error);
+ return TEST_FAILED;
+ }
+ log_debug("STEP2: Find UNIT left bound\n");
+ /* STEP2: Find UNIT left bound */
+ while ((dqs_unit_delay_index >= 0) &&
+ !left_unit_bound_found) {
+ DQS_unit_delay(phy, byte,
+ dqs_unit_delay_index);
+ DQS_phase_delay(phy, byte, phase_idx);
+ BIST_test(phy, byte, &result);
+ success = result.test_result;
+ if (!success) {
+ left_bound.unit =
+ ++dqs_unit_delay_index;
+ left_unit_bound_found = 1;
+ left_bound_found = 1;
+ } else if (success) {
+ dqs_unit_delay_index--;
+ }
+ }
+
+ /* If not found, lets take 0 */
+ if (!left_unit_bound_found)
+ left_bound.unit = 0;
+
+ if (ctrlc()) {
+ sprintf(string, "interrupted at byte %d/%d, error=%d",
+ byte + 1, nb_bytes, error);
+ return TEST_FAILED;
+ }
+ log_debug("STEP3: Find PHase right bound\n");
+ /* STEP3: Find PHase right bound, start with "pass"
+ * condition
+ */
+
+ /* Set DQS phase delay to the pass value. */
+ phase_idx = phase_idx_pass;
+
+ /* Set DQS unit delay to the pass value. */
+ dqs_unit_delay_index = dqs_unit_delay_index_pass;
+
+ while ((phase_idx <= MAX_DQS_PHASE_IDX) &&
+ !right_phase_bound_found) {
+ DQS_unit_delay(phy, byte,
+ dqs_unit_delay_index);
+ DQS_phase_delay(phy, byte, phase_idx);
+ BIST_test(phy, byte, &result);
+ success = result.test_result;
+ if (!success) {
+ /* the last pass condition */
+ right_bound.phase = --phase_idx;
+ right_phase_bound_found = 1;
+ } else if (success) {
+ phase_idx++;
+ }
+ }
+
+ /* If not found, lets take the max value */
+ if (!right_phase_bound_found) {
+ right_bound.phase = MAX_DQS_PHASE_IDX;
+ phase_idx = MAX_DQS_PHASE_IDX;
+ }
+
+ if (ctrlc()) {
+ sprintf(string, "interrupted at byte %d/%d, error=%d",
+ byte + 1, nb_bytes, error);
+ return TEST_FAILED;
+ }
+ log_debug("STEP4: Find UNIT right bound\n");
+ /* STEP4: Find UNIT right bound */
+ while ((dqs_unit_delay_index <= MAX_DQS_UNIT_IDX) &&
+ !right_unit_bound_found) {
+ DQS_unit_delay(phy, byte,
+ dqs_unit_delay_index);
+ DQS_phase_delay(phy, byte, phase_idx);
+ BIST_test(phy, byte, &result);
+ success = result.test_result;
+ if (!success) {
+ right_bound.unit =
+ --dqs_unit_delay_index;
+ right_unit_bound_found = 1;
+ right_bound_found = 1;
+ } else if (success) {
+ dqs_unit_delay_index++;
+ }
+ }
+ /* If not found, lets take the max value */
+ if (!right_unit_bound_found)
+ right_bound.unit = MAX_DQS_UNIT_IDX;
+
+ /* If we found a regular FAil Pass FAil pattern
+ * FFPPPPPPFF
+ * OR PPPPPFF Or FFPPPPP
+ */
+
+ if (left_bound_found || right_bound_found) {
+ eye_training_val[byte][0] = (right_bound.phase +
+ left_bound.phase) / 2;
+ eye_training_val[byte][1] = (right_bound.unit +
+ left_bound.unit) / 2;
+
+ /* If we already lost 1/2PHASE Tuning,
+ * let's try to recover by ++ on unit
+ */
+ if (((right_bound.phase + left_bound.phase) % 2 == 1) &&
+ eye_training_val[byte][1] != MAX_DQS_UNIT_IDX)
+ eye_training_val[byte][1]++;
+ log_debug("** found phase : %d - %d & unit %d - %d\n",
+ right_bound.phase, left_bound.phase,
+ right_bound.unit, left_bound.unit);
+ log_debug("** calculating mid region: phase: %d unit: %d (nominal is 3)\n",
+ eye_training_val[byte][0],
+ eye_training_val[byte][1]);
+ } else {
+ /* PPPPPPPPPP, we're already good.
+ * Set nominal values.
+ */
+ eye_training_val[byte][0] = 3;
+ eye_training_val[byte][1] = 3;
+ }
+ DQS_phase_delay(phy, byte, eye_training_val[byte][0]);
+ DQS_unit_delay(phy, byte, eye_training_val[byte][1]);
+
+ printf("Byte %d, DQS unit = %d, phase = %d\n",
+ byte,
+ eye_training_val[byte][1],
+ eye_training_val[byte][0]);
+ }
+
+ if (error) {
+ sprintf(string, "error = %d", error);
+ return TEST_FAILED;
+ }
+
+ return TEST_PASSED;
+}
+
+static void display_reg_results(struct stm32mp1_ddrphy *phy, u8 byte)
+{
+ u8 i = 0;
+
+ printf("Byte %d Dekew result, bit0 delay, bit1 delay...bit8 delay\n ",
+ byte);
+
+ for (i = 0; i < 8; i++)
+ printf("%d ", DQ_unit_index(phy, byte, i));
+ printf("\n");
+
+ printf("dxndllcr: [%08x] val:%08x\n",
+ DXNDLLCR(phy, byte),
+ readl(DXNDLLCR(phy, byte)));
+ printf("dxnqdstr: [%08x] val:%08x\n",
+ DXNDQSTR(phy, byte),
+ readl(DXNDQSTR(phy, byte)));
+ printf("dxndqtr: [%08x] val:%08x\n",
+ DXNDQTR(phy, byte),
+ readl(DXNDQTR(phy, byte)));
+}
+
+/* analyse the dgs gating log table, and determine the midpoint.*/
+static u8 set_midpoint_read_dqs_gating(struct stm32mp1_ddrphy *phy, u8 byte,
+ u8 dqs_gating[NUM_BYTES]
+ [MAX_GSL_IDX + 1]
+ [MAX_GPS_IDX + 1])
+{
+ /* stores the dqs gate values (gsl index, gps index) */
+ u8 dqs_gate_values[NUM_BYTES][2];
+ u8 gsl_idx, gps_idx = 0;
+ u8 left_bound_idx[2] = {0, 0};
+ u8 right_bound_idx[2] = {0, 0};
+ u8 left_bound_found = 0;
+ u8 right_bound_found = 0;
+ u8 intermittent = 0;
+ u8 value;
+
+ for (gsl_idx = 0; gsl_idx <= MAX_GSL_IDX; gsl_idx++) {
+ for (gps_idx = 0; gps_idx <= MAX_GPS_IDX; gps_idx++) {
+ value = dqs_gating[byte][gsl_idx][gps_idx];
+ if (value == 1 && left_bound_found == 0) {
+ left_bound_idx[0] = gsl_idx;
+ left_bound_idx[1] = gps_idx;
+ left_bound_found = 1;
+ } else if (value == 0 &&
+ left_bound_found == 1 &&
+ !right_bound_found) {
+ if (gps_idx == 0) {
+ right_bound_idx[0] = gsl_idx - 1;
+ right_bound_idx[1] = MAX_GPS_IDX;
+ } else {
+ right_bound_idx[0] = gsl_idx;
+ right_bound_idx[1] = gps_idx - 1;
+ }
+ right_bound_found = 1;
+ } else if (value == 1 &&
+ right_bound_found == 1) {
+ intermittent = 1;
+ }
+ }
+ }
+
+ /* if only ppppppp is found, there is no mid region. */
+ if (left_bound_idx[0] == 0 && left_bound_idx[1] == 0 &&
+ right_bound_idx[0] == 0 && right_bound_idx[1] == 0)
+ intermittent = 1;
+
+ /*if we found a regular fail pass fail pattern ffppppppff
+ * or pppppff or ffppppp
+ */
+ if (!intermittent) {
+ /*if we found a regular fail pass fail pattern ffppppppff
+ * or pppppff or ffppppp
+ */
+ if (left_bound_found || right_bound_found) {
+ log_debug("idx0(%d): %d %d idx1(%d) : %d %d\n",
+ left_bound_found,
+ right_bound_idx[0], left_bound_idx[0],
+ right_bound_found,
+ right_bound_idx[1], left_bound_idx[1]);
+ dqs_gate_values[byte][0] =
+ (right_bound_idx[0] + left_bound_idx[0]) / 2;
+ dqs_gate_values[byte][1] =
+ (right_bound_idx[1] + left_bound_idx[1]) / 2;
+ /* if we already lost 1/2gsl tuning,
+ * let's try to recover by ++ on gps
+ */
+ if (((right_bound_idx[0] +
+ left_bound_idx[0]) % 2 == 1) &&
+ dqs_gate_values[byte][1] != MAX_GPS_IDX)
+ dqs_gate_values[byte][1]++;
+ /* if we already lost 1/2gsl tuning and gps is on max*/
+ else if (((right_bound_idx[0] +
+ left_bound_idx[0]) % 2 == 1) &&
+ dqs_gate_values[byte][1] == MAX_GPS_IDX) {
+ dqs_gate_values[byte][1] = 0;
+ dqs_gate_values[byte][0]++;
+ }
+ /* if we have gsl left and write limit too close
+ * (difference=1)
+ */
+ if (((right_bound_idx[0] - left_bound_idx[0]) == 1)) {
+ dqs_gate_values[byte][1] = (left_bound_idx[1] +
+ right_bound_idx[1] +
+ 4) / 2;
+ if (dqs_gate_values[byte][1] >= 4) {
+ dqs_gate_values[byte][0] =
+ right_bound_idx[0];
+ dqs_gate_values[byte][1] -= 4;
+ } else {
+ dqs_gate_values[byte][0] =
+ left_bound_idx[0];
+ }
+ }
+ log_debug("*******calculating mid region: system latency: %d phase: %d********\n",
+ dqs_gate_values[byte][0],
+ dqs_gate_values[byte][1]);
+ log_debug("*******the nominal values were system latency: 0 phase: 2*******\n");
+ }
+ } else {
+ /* if intermitant, restore defaut values */
+ log_debug("dqs gating:no regular fail/pass/fail found. defaults values restored.\n");
+ dqs_gate_values[byte][0] = 0;
+ dqs_gate_values[byte][1] = 2;
+ }
+ set_r0dgsl_delay(phy, byte, dqs_gate_values[byte][0]);
+ set_r0dgps_delay(phy, byte, dqs_gate_values[byte][1]);
+ printf("Byte %d, R0DGSL = %d, R0DGPS = %d\n",
+ byte, dqs_gate_values[byte][0], dqs_gate_values[byte][1]);
+
+ /* return 0 if intermittent or if both left_bound
+ * and right_bound are not found
+ */
+ return !(intermittent || (left_bound_found && right_bound_found));
+}
+
+static enum test_result read_dqs_gating(struct stm32mp1_ddrctl *ctl,
+ struct stm32mp1_ddrphy *phy,
+ char *string)
+{
+ /* stores the log of pass/fail */
+ u8 dqs_gating[NUM_BYTES][MAX_GSL_IDX + 1][MAX_GPS_IDX + 1];
+ u8 byte, gsl_idx, gps_idx = 0;
+ struct BIST_result result;
+ u8 success = 0;
+ u8 nb_bytes = get_nb_bytes(ctl);
+
+ memset(dqs_gating, 0x0, sizeof(dqs_gating));
+
+ /*disable dqs drift compensation*/
+ clrbits_le32(&phy->pgcr, DDRPHYC_PGCR_DFTCMP);
+ /*disable all bytes*/
+ /* disable automatic power down of dll and ios when disabling a byte
+ * (to avoid having to add programming and delay
+ * for a dll re-lock when later re-enabling a disabled byte lane)
+ */
+ clrbits_le32(&phy->pgcr, DDRPHYC_PGCR_PDDISDX);
+
+ /* disable all data bytes */
+ clrbits_le32(&phy->dx0gcr, DDRPHYC_DXNGCR_DXEN);
+ clrbits_le32(&phy->dx1gcr, DDRPHYC_DXNGCR_DXEN);
+ clrbits_le32(&phy->dx2gcr, DDRPHYC_DXNGCR_DXEN);
+ clrbits_le32(&phy->dx3gcr, DDRPHYC_DXNGCR_DXEN);
+
+ /* config the bist block */
+ config_BIST(ctl, phy);
+
+ for (byte = 0; byte < nb_bytes; byte++) {
+ if (ctrlc()) {
+ sprintf(string, "interrupted at byte %d/%d",
+ byte + 1, nb_bytes);
+ return TEST_FAILED;
+ }
+ /* enable byte x (dxngcr, bit dxen) */
+ setbits_le32(DXNGCR(phy, byte), DDRPHYC_DXNGCR_DXEN);
+
+ /* select the byte lane for comparison of read data */
+ BIST_datx8_sel(phy, byte);
+ for (gsl_idx = 0; gsl_idx <= MAX_GSL_IDX; gsl_idx++) {
+ for (gps_idx = 0; gps_idx <= MAX_GPS_IDX; gps_idx++) {
+ if (ctrlc()) {
+ sprintf(string,
+ "interrupted at byte %d/%d",
+ byte + 1, nb_bytes);
+ return TEST_FAILED;
+ }
+ /* write cfg to dxndqstr */
+ set_r0dgsl_delay(phy, byte, gsl_idx);
+ set_r0dgps_delay(phy, byte, gps_idx);
+
+ BIST_test(phy, byte, &result);
+ success = result.test_result;
+ if (success)
+ dqs_gating[byte][gsl_idx][gps_idx] = 1;
+ itm_soft_reset(phy);
+ }
+ }
+ set_midpoint_read_dqs_gating(phy, byte, dqs_gating);
+ /* dummy reads */
+ readl(0xc0000000);
+ readl(0xc0000000);
+ }
+
+ /* re-enable drift compensation */
+ /* setbits_le32(&phy->pgcr, DDRPHYC_PGCR_DFTCMP); */
+ return TEST_PASSED;
+}
+
+/****************************************************************
+ * TEST
+ ****************************************************************
+ */
+static enum test_result do_read_dqs_gating(struct stm32mp1_ddrctl *ctl,
+ struct stm32mp1_ddrphy *phy,
+ char *string, int argc,
+ char *argv[])
+{
+ u32 rfshctl3 = readl(&ctl->rfshctl3);
+ u32 pwrctl = readl(&ctl->pwrctl);
+ u32 derateen = readl(&ctl->derateen);
+ enum test_result res;
+
+ writel(0x0, &ctl->derateen);
+ stm32mp1_refresh_disable(ctl);
+
+ res = read_dqs_gating(ctl, phy, string);
+
+ stm32mp1_refresh_restore(ctl, rfshctl3, pwrctl);
+ writel(derateen, &ctl->derateen);
+
+ return res;
+}
+
+static enum test_result do_bit_deskew(struct stm32mp1_ddrctl *ctl,
+ struct stm32mp1_ddrphy *phy,
+ char *string, int argc, char *argv[])
+{
+ u32 rfshctl3 = readl(&ctl->rfshctl3);
+ u32 pwrctl = readl(&ctl->pwrctl);
+ u32 derateen = readl(&ctl->derateen);
+ enum test_result res;
+
+ writel(0x0, &ctl->derateen);
+ stm32mp1_refresh_disable(ctl);
+
+ res = bit_deskew(ctl, phy, string);
+
+ stm32mp1_refresh_restore(ctl, rfshctl3, pwrctl);
+ writel(derateen, &ctl->derateen);
+
+ return res;
+}
+
+static enum test_result do_eye_training(struct stm32mp1_ddrctl *ctl,
+ struct stm32mp1_ddrphy *phy,
+ char *string, int argc, char *argv[])
+{
+ u32 rfshctl3 = readl(&ctl->rfshctl3);
+ u32 pwrctl = readl(&ctl->pwrctl);
+ u32 derateen = readl(&ctl->derateen);
+ enum test_result res;
+
+ writel(0x0, &ctl->derateen);
+ stm32mp1_refresh_disable(ctl);
+
+ res = eye_training(ctl, phy, string);
+
+ stm32mp1_refresh_restore(ctl, rfshctl3, pwrctl);
+ writel(derateen, &ctl->derateen);
+
+ return res;
+}
+
+static enum test_result do_display(struct stm32mp1_ddrctl *ctl,
+ struct stm32mp1_ddrphy *phy,
+ char *string, int argc, char *argv[])
+{
+ int byte;
+ u8 nb_bytes = get_nb_bytes(ctl);
+
+ for (byte = 0; byte < nb_bytes; byte++)
+ display_reg_results(phy, byte);
+
+ return TEST_PASSED;
+}
+
+static enum test_result do_bist_config(struct stm32mp1_ddrctl *ctl,
+ struct stm32mp1_ddrphy *phy,
+ char *string, int argc, char *argv[])
+{
+ unsigned long value;
+
+ if (argc > 0) {
+ if (strict_strtoul(argv[0], 0, &value) < 0) {
+ sprintf(string, "invalid nbErr %s", argv[0]);
+ return TEST_FAILED;
+ }
+ BIST_error_max = value;
+ }
+ if (argc > 1) {
+ if (strict_strtoul(argv[1], 0, &value) < 0) {
+ sprintf(string, "invalid Seed %s", argv[1]);
+ return TEST_FAILED;
+ }
+ BIST_seed = value;
+ }
+ printf("Bist.nbErr = %d\n", BIST_error_max);
+ if (BIST_seed)
+ printf("Bist.Seed = 0x%x\n", BIST_seed);
+ else
+ printf("Bist.Seed = random\n");
+
+ return TEST_PASSED;
+}
+
+/****************************************************************
+ * TEST Description
+ ****************************************************************
+ */
+
+const struct test_desc tuning[] = {
+ {do_read_dqs_gating, "Read DQS gating",
+ "software read DQS Gating", "", 0 },
+ {do_bit_deskew, "Bit de-skew", "", "", 0 },
+ {do_eye_training, "Eye Training", "or DQS training", "", 0 },
+ {do_display, "Display registers", "", "", 0 },
+ {do_bist_config, "Bist config", "[nbErr] [seed]",
+ "configure Bist test", 2},
+};
+
+const int tuning_nb = ARRAY_SIZE(tuning);