Merge tag 'u-boot-stm32-20190523' of https://github.com/pchotard/u-boot
authorTom Rini <trini@konsulko.com>
Fri, 24 May 2019 12:13:27 +0000 (08:13 -0400)
committerTom Rini <trini@konsulko.com>
Fri, 24 May 2019 12:13:27 +0000 (08:13 -0400)
- Add various STM32MP1 fixes for serial, env, clk, board, i2c ...

- Add STM32MP1 DDR driver update:
These update introduce the DDR interactive mode described in:
https://wiki.st.com/stm32mpu/index.php/U-Boot_SPL:_DDR_interactive_mode

This mode is used by the CubeMX: DDR tuning tool.
https://wiki.st.com/stm32mpu/index.php/STM32CubeMX

The DDR interactive mode is NOT activated by default because
it increase the SPL size and slow down the boot time
(200ms wait added).

32 files changed:
Kconfig
arch/arm/Kconfig
arch/arm/cpu/armv7/arch_timer.c
arch/arm/dts/stm32mp15-ddr3-1x4Gb-1066-binG.dtsi
arch/arm/dts/stm32mp15-ddr3-2x4Gb-1066-binG.dtsi
arch/arm/mach-stm32mp/Kconfig
arch/arm/mach-stm32mp/bsec.c
arch/arm/mach-stm32mp/include/mach/ddr.h
arch/arm/mach-stm32mp/include/mach/stm32.h
arch/arm/mach-stm32mp/psci.c
board/st/stm32mp1/board.c
common/Kconfig
common/Makefile
doc/device-tree-bindings/clock/st,stm32mp1.txt
doc/device-tree-bindings/memory-controllers/st,stm32mp1-ddr.txt
drivers/clk/clk_stm32mp1.c
drivers/i2c/stm32f7_i2c.c
drivers/ram/stm32mp1/Kconfig
drivers/ram/stm32mp1/Makefile
drivers/ram/stm32mp1/stm32mp1_ddr.c
drivers/ram/stm32mp1/stm32mp1_ddr.h
drivers/ram/stm32mp1/stm32mp1_ddr_regs.h
drivers/ram/stm32mp1/stm32mp1_interactive.c [new file with mode: 0644]
drivers/ram/stm32mp1/stm32mp1_ram.c
drivers/ram/stm32mp1/stm32mp1_tests.c [new file with mode: 0644]
drivers/ram/stm32mp1/stm32mp1_tests.h [new file with mode: 0644]
drivers/ram/stm32mp1/stm32mp1_tuning.c [new file with mode: 0644]
drivers/serial/serial_stm32.c
env/Kconfig
env/common.c
include/configs/stm32mp1.h
tools/stm32image.c

diff --git a/Kconfig b/Kconfig
index 5f5c5ccfd6b6b51f28ee433f7b7cfd528ef91755..fcde0c35e2c4ee85ece6f982251352dcb3f2feff 100644 (file)
--- a/Kconfig
+++ b/Kconfig
@@ -147,7 +147,7 @@ config SYS_MALLOC_F_LEN
 
 config SYS_MALLOC_LEN
        hex "Define memory for Dynamic allocation"
-       depends on ARCH_ZYNQ || ARCH_VERSAL
+       depends on ARCH_ZYNQ || ARCH_VERSAL || ARCH_STM32MP
        help
          This defines memory to be allocated for Dynamic allocation
          TODO: Use for other architectures
index efb9aab39061dbe42552e5d48c1e587cf48edfc2..01ff57cf1bec359d89620df661a9d16c434ac55e 100644 (file)
@@ -1488,6 +1488,7 @@ config ARCH_STM32MP
        imply CMD_DM
        imply CMD_POWEROFF
        imply ENV_VARS_UBOOT_RUNTIME_CONFIG
+       imply USE_PREBOOT
        help
          Support for STM32MP SoC family developed by STMicroelectronics,
          MPUs based on ARM cortex A core
index 3db31c020952389b3fe9952bcba45802746e1453..5de63053d5934ad8c3fac1dcf27273c014e4a4eb 100644 (file)
@@ -49,6 +49,9 @@ unsigned long long get_ticks(void)
 
 ulong timer_get_boot_us(void)
 {
+       if (!gd->arch.timer_rate_hz)
+               timer_init();
+
        return lldiv(get_ticks(), gd->arch.timer_rate_hz / 1000000);
 }
 
index 7d9b95ccf166abb997204e30f15ec0c72e9b27fa..dc30360b0a413af913d5a52354fcfd4c8001e28d 100644 (file)
@@ -16,8 +16,8 @@
  * address mapping : RBC
  * Tc > + 85C : N
  */
-#define DDR_MEM_NAME "DDR3-1066/888 bin G 1x4Gb 533MHz v1.43"
-#define DDR_MEM_SPEED 533
+#define DDR_MEM_NAME "DDR3-1066/888 bin G 1x4Gb 533MHz v1.44"
+#define DDR_MEM_SPEED 533000
 #define DDR_MEM_SIZE 0x20000000
 
 #define DDR_MSTR 0x00041401
 #define DDR_DX1DLLCR 0x40000000
 #define DDR_DX1DQTR 0xFFFFFFFF
 #define DDR_DX1DQSTR 0x3DB02000
-#define DDR_DX2GCR 0x0000CE81
+#define DDR_DX2GCR 0x0000CE80
 #define DDR_DX2DLLCR 0x40000000
 #define DDR_DX2DQTR 0xFFFFFFFF
 #define DDR_DX2DQSTR 0x3DB02000
-#define DDR_DX3GCR 0x0000CE81
+#define DDR_DX3GCR 0x0000CE80
 #define DDR_DX3DLLCR 0x40000000
 #define DDR_DX3DQTR 0xFFFFFFFF
 #define DDR_DX3DQSTR 0x3DB02000
index 8a5a821ec4e28bfd300c4ac000cc8a81ba3f8c11..8158a56f1350cd488d97563e228ecdcaa7d6ab22 100644 (file)
@@ -1,9 +1,8 @@
 // SPDX-License-Identifier: GPL-2.0+ OR BSD-3-Clause
 /*
  * Copyright (C) 2018, STMicroelectronics - All Rights Reserved
- */
-
-/* STM32MP157C ED1 and ED2 BOARD configuration
+ *
+ * STM32MP157C ED1 BOARD configuration
  * 2x DDR3L 4Gb each, 16-bit, 533MHz, Single Die Package in flyby topology.
  * Reference used NT5CC256M16DP-DI from NANYA
  *
  * timing mode optimized
  * Scheduling/QoS options : type = 2
  * address mapping : RBC
+ * Tc > + 85C : N
  */
 
-#define DDR_MEM_NAME "DDR3-1066 bin G 2x4Gb 533MHz v1.36"
-#define DDR_MEM_SPEED 533
+#define DDR_MEM_NAME "DDR3-1066/888 bin G 2x4Gb 533MHz v1.44"
+#define DDR_MEM_SPEED 533000
 #define DDR_MEM_SIZE 0x40000000
 
 #define DDR_MSTR 0x00040401
@@ -62,7 +62,7 @@
 #define DDR_ADDRMAP11 0x00000000
 #define DDR_ODTCFG 0x06000600
 #define DDR_ODTMAP 0x00000001
-#define DDR_SCHED 0x00001201
+#define DDR_SCHED 0x00000C01
 #define DDR_SCHED1 0x00000000
 #define DDR_PERFHPR1 0x01000001
 #define DDR_PERFLPR1 0x08000200
 #define DDR_PCCFG 0x00000010
 #define DDR_PCFGR_0 0x00010000
 #define DDR_PCFGW_0 0x00000000
-#define DDR_PCFGQOS0_0 0x02100B03
+#define DDR_PCFGQOS0_0 0x02100C03
 #define DDR_PCFGQOS1_0 0x00800100
-#define DDR_PCFGWQOS0_0 0x01100B03
+#define DDR_PCFGWQOS0_0 0x01100C03
 #define DDR_PCFGWQOS1_0 0x01000200
 #define DDR_PCFGR_1 0x00010000
 #define DDR_PCFGW_1 0x00000000
-#define DDR_PCFGQOS0_1 0x02100B03
-#define DDR_PCFGQOS1_1 0x00800100
-#define DDR_PCFGWQOS0_1 0x01100B03
+#define DDR_PCFGQOS0_1 0x02100C03
+#define DDR_PCFGQOS1_1 0x00800040
+#define DDR_PCFGWQOS0_1 0x01100C03
 #define DDR_PCFGWQOS1_1 0x01000200
 #define DDR_PGCR 0x01442E02
 #define DDR_PTR0 0x0022AA5B
 #define DDR_MR2 0x00000208
 #define DDR_MR3 0x00000000
 #define DDR_ODTCR 0x00010000
-#define DDR_ZQ0CR1 0x0000005B
+#define DDR_ZQ0CR1 0x00000038
 #define DDR_DX0GCR 0x0000CE81
 #define DDR_DX0DLLCR 0x40000000
 #define DDR_DX0DQTR 0xFFFFFFFF
index 73aa382712268ddcb14477e651e7f6b31b0566d6..77f66c65c03ecbbce3c55b2df38b50b5351027b6 100644 (file)
@@ -17,12 +17,20 @@ config SPL
        select SPL_DM_RESET
        select SPL_SERIAL_SUPPORT
        select SPL_SYSCON
+       imply BOOTSTAGE_STASH if SPL_BOOTSTAGE
+       imply SPL_BOOTSTAGE if BOOTSTAGE
        imply SPL_DISPLAY_PRINT
        imply SPL_LIBDISK_SUPPORT
 
 config SYS_SOC
        default "stm32mp"
 
+config SYS_MALLOC_LEN
+       default 0x2000000
+
+config ENV_SIZE
+       default 0x1000
+
 config TARGET_STM32MP1
        bool "Support stm32mp1xx"
        select ARCH_SUPPORT_PSCI if !STM32MP1_TRUSTED
@@ -33,6 +41,10 @@ config TARGET_STM32MP1
        select STM32_RCC
        select STM32_RESET
        select SYS_ARCH_TIMER
+       imply BOOTCOUNT_LIMIT
+       imply BOOTSTAGE
+       imply CMD_BOOTCOUNT
+       imply CMD_BOOTSTAGE
        imply SYSRESET_PSCI if STM32MP1_TRUSTED
        imply SYSRESET_SYSCON if !STM32MP1_TRUSTED
        help
@@ -70,6 +82,18 @@ config SYS_MMCSD_RAW_MODE_U_BOOT_PARTITION_MMC2
          Partition on the second MMC to load U-Boot from when the MMC is being
          used in raw mode
 
+config BOOTSTAGE_STASH_ADDR
+       default 0xC3000000
+
+if BOOTCOUNT_LIMIT
+config SYS_BOOTCOUNT_SINGLEWORD
+       default y
+
+# TAMP_BOOTCOUNT = TAMP_BACKUP_REGISTER(21)
+config SYS_BOOTCOUNT_ADDR
+       default 0x5C00A154
+endif
+
 if DEBUG_UART
 
 config DEBUG_UART_BOARD_INIT
index 9ed8d8c56c3c4c811eeef7b75867de1b8b7853eb..0166649685413209dea179fc8b101cc2bd15a902 100644 (file)
@@ -7,9 +7,9 @@
 #include <dm.h>
 #include <misc.h>
 #include <asm/io.h>
-#include <linux/iopoll.h>
 #include <asm/arch/stm32mp1_smc.h>
 #include <linux/arm-smccc.h>
+#include <linux/iopoll.h>
 
 #define BSEC_OTP_MAX_VALUE             95
 
index 18575842ba9a7f098f19605b4f3210c024a27ebf..b8a17cfbddb8ac1976dd5fc3950c99875b58ea1d 100644 (file)
@@ -6,6 +6,13 @@
 #ifndef __MACH_STM32MP_DDR_H_
 #define __MACH_STM32MP_DDR_H_
 
-int board_ddr_power_init(void);
+/* DDR power initializations */
+enum ddr_type {
+       STM32MP_DDR3,
+       STM32MP_LPDDR2,
+       STM32MP_LPDDR3,
+};
+
+int board_ddr_power_init(enum ddr_type ddr_type);
 
 #endif
index c526c88e3ee9caf25558c66c80d976c8bacdb37b..67953520448572814c91b22d8d80d8afe4198790 100644 (file)
@@ -88,6 +88,7 @@ enum boot_device {
 #define TAMP_BACKUP_MAGIC_NUMBER       TAMP_BACKUP_REGISTER(4)
 #define TAMP_BACKUP_BRANCH_ADDRESS     TAMP_BACKUP_REGISTER(5)
 #define TAMP_BOOT_CONTEXT              TAMP_BACKUP_REGISTER(20)
+#define TAMP_BOOTCOUNT                 TAMP_BACKUP_REGISTER(21)
 
 #define TAMP_BOOT_MODE_MASK            GENMASK(15, 8)
 #define TAMP_BOOT_MODE_SHIFT           8
index c2dff38c368c9f6c540651157212015d14519cc9..139bb09292263c5e8db9fb7e40a1d63ed607e3c5 100644 (file)
@@ -47,14 +47,14 @@ static u32 __secure stm32mp_get_gicd_base_address(void)
        return (periphbase & CBAR_MASK) + GIC_DIST_OFFSET;
 }
 
-static void __secure stm32mp_smp_kick_all_cpus(void)
+static void __secure stm32mp_raise_sgi0(int cpu)
 {
        u32 gic_dist_addr;
 
        gic_dist_addr = stm32mp_get_gicd_base_address();
 
-       /* kick all CPUs (except this one) by writing to GICD_SGIR */
-       writel(1U << 24, gic_dist_addr + GICD_SGIR);
+       /* ask cpu with SGI0 */
+       writel((BIT(cpu) << 16), gic_dist_addr + GICD_SGIR);
 }
 
 void __secure psci_arch_cpu_entry(void)
@@ -62,6 +62,9 @@ void __secure psci_arch_cpu_entry(void)
        u32 cpu = psci_get_cpu_id();
 
        psci_set_state(cpu, PSCI_AFFINITY_LEVEL_ON);
+
+       /* reset magic in TAMP register */
+       writel(0xFFFFFFFF, TAMP_BACKUP_MAGIC_NUMBER);
 }
 
 int __secure psci_features(u32 function_id, u32 psci_fid)
@@ -127,6 +130,16 @@ int __secure psci_cpu_on(u32 function_id, u32 target_cpu, u32 pc,
        if (psci_state[cpu] == PSCI_AFFINITY_LEVEL_ON)
                return ARM_PSCI_RET_ALREADY_ON;
 
+       /* reset magic in TAMP register */
+       if (readl(TAMP_BACKUP_MAGIC_NUMBER))
+               writel(0xFFFFFFFF, TAMP_BACKUP_MAGIC_NUMBER);
+       /*
+        * ROM code need a first SGI0 after core reset
+        * core is ready when magic is set to 0 in ROM code
+        */
+       while (readl(TAMP_BACKUP_MAGIC_NUMBER))
+               stm32mp_raise_sgi0(cpu);
+
        /* store target PC and context id*/
        psci_save(cpu, pc, context_id);
 
@@ -142,7 +155,8 @@ int __secure psci_cpu_on(u32 function_id, u32 target_cpu, u32 pc,
                writel(BOOT_API_A7_CORE0_MAGIC_NUMBER,
                       TAMP_BACKUP_MAGIC_NUMBER);
 
-       stm32mp_smp_kick_all_cpus();
+       /* Generate an IT to start the core */
+       stm32mp_raise_sgi0(cpu);
 
        return ARM_PSCI_RET_SUCCESS;
 }
index 5c1acca20d79dd3213357d99f99b02a09affe550..c3d832f58489bd3ca5e5b37ad6696dc94c4eb727 100644 (file)
@@ -38,9 +38,10 @@ void board_debug_uart_init(void)
 #endif
 
 #ifdef CONFIG_PMIC_STPMIC1
-int board_ddr_power_init(void)
+int board_ddr_power_init(enum ddr_type ddr_type)
 {
        struct udevice *dev;
+       bool buck3_at_1800000v = false;
        int ret;
 
        ret = uclass_get_device_by_driver(UCLASS_PMIC,
@@ -49,53 +50,127 @@ int board_ddr_power_init(void)
                /* No PMIC on board */
                return 0;
 
-       /* VTT = Set LDO3 to sync mode */
-       ret = pmic_reg_read(dev, STPMIC1_LDOX_MAIN_CR(STPMIC1_LDO3));
-       if (ret < 0)
-               return ret;
-
-       ret &= ~STPMIC1_LDO3_MODE;
-       ret &= ~STPMIC1_LDO12356_VOUT_MASK;
-       ret |= STPMIC1_LDO_VOUT(STPMIC1_LDO3_DDR_SEL);
-
-       ret = pmic_reg_write(dev, STPMIC1_LDOX_MAIN_CR(STPMIC1_LDO3),
-                            ret);
-       if (ret < 0)
-               return ret;
-
-       /* VDD_DDR = Set BUCK2 to 1.35V */
-       ret = pmic_clrsetbits(dev,
-                             STPMIC1_BUCKX_MAIN_CR(STPMIC1_BUCK2),
-                             STPMIC1_BUCK_VOUT_MASK,
-                             STPMIC1_BUCK2_1350000V);
-       if (ret < 0)
-               return ret;
-
-       /* Enable VDD_DDR = BUCK2 */
-       ret = pmic_clrsetbits(dev,
-                             STPMIC1_BUCKX_MAIN_CR(STPMIC1_BUCK2),
-                             STPMIC1_BUCK_ENA, STPMIC1_BUCK_ENA);
-       if (ret < 0)
-               return ret;
-
-       mdelay(STPMIC1_DEFAULT_START_UP_DELAY_MS);
-
-       /* Enable VREF */
-       ret = pmic_clrsetbits(dev, STPMIC1_REFDDR_MAIN_CR,
-                             STPMIC1_VREF_ENA, STPMIC1_VREF_ENA);
-       if (ret < 0)
-               return ret;
-
-       mdelay(STPMIC1_DEFAULT_START_UP_DELAY_MS);
-
-       /* Enable LDO3 */
-       ret = pmic_clrsetbits(dev,
-                             STPMIC1_LDOX_MAIN_CR(STPMIC1_LDO3),
-                             STPMIC1_LDO_ENA, STPMIC1_LDO_ENA);
-       if (ret < 0)
-               return ret;
-
-       mdelay(STPMIC1_DEFAULT_START_UP_DELAY_MS);
+       switch (ddr_type) {
+       case STM32MP_DDR3:
+               /* VTT = Set LDO3 to sync mode */
+               ret = pmic_reg_read(dev, STPMIC1_LDOX_MAIN_CR(STPMIC1_LDO3));
+               if (ret < 0)
+                       return ret;
+
+               ret &= ~STPMIC1_LDO3_MODE;
+               ret &= ~STPMIC1_LDO12356_VOUT_MASK;
+               ret |= STPMIC1_LDO_VOUT(STPMIC1_LDO3_DDR_SEL);
+
+               ret = pmic_reg_write(dev, STPMIC1_LDOX_MAIN_CR(STPMIC1_LDO3),
+                                    ret);
+               if (ret < 0)
+                       return ret;
+
+               /* VDD_DDR = Set BUCK2 to 1.35V */
+               ret = pmic_clrsetbits(dev,
+                                     STPMIC1_BUCKX_MAIN_CR(STPMIC1_BUCK2),
+                                     STPMIC1_BUCK_VOUT_MASK,
+                                     STPMIC1_BUCK2_1350000V);
+               if (ret < 0)
+                       return ret;
+
+               /* Enable VDD_DDR = BUCK2 */
+               ret = pmic_clrsetbits(dev,
+                                     STPMIC1_BUCKX_MAIN_CR(STPMIC1_BUCK2),
+                                     STPMIC1_BUCK_ENA, STPMIC1_BUCK_ENA);
+               if (ret < 0)
+                       return ret;
+
+               mdelay(STPMIC1_DEFAULT_START_UP_DELAY_MS);
+
+               /* Enable VREF */
+               ret = pmic_clrsetbits(dev, STPMIC1_REFDDR_MAIN_CR,
+                                     STPMIC1_VREF_ENA, STPMIC1_VREF_ENA);
+               if (ret < 0)
+                       return ret;
+
+               mdelay(STPMIC1_DEFAULT_START_UP_DELAY_MS);
+
+               /* Enable VTT = LDO3 */
+               ret = pmic_clrsetbits(dev,
+                                     STPMIC1_LDOX_MAIN_CR(STPMIC1_LDO3),
+                                     STPMIC1_LDO_ENA, STPMIC1_LDO_ENA);
+               if (ret < 0)
+                       return ret;
+
+               mdelay(STPMIC1_DEFAULT_START_UP_DELAY_MS);
+
+               break;
+
+       case STM32MP_LPDDR2:
+       case STM32MP_LPDDR3:
+               /*
+                * configure VDD_DDR1 = LDO3
+                * Set LDO3 to 1.8V
+                * + bypass mode if BUCK3 = 1.8V
+                * + normal mode if BUCK3 != 1.8V
+                */
+               ret = pmic_reg_read(dev,
+                                   STPMIC1_BUCKX_MAIN_CR(STPMIC1_BUCK3));
+               if (ret < 0)
+                       return ret;
+
+               if ((ret & STPMIC1_BUCK3_1800000V) == STPMIC1_BUCK3_1800000V)
+                       buck3_at_1800000v = true;
+
+               ret = pmic_reg_read(dev, STPMIC1_LDOX_MAIN_CR(STPMIC1_LDO3));
+               if (ret < 0)
+                       return ret;
+
+               ret &= ~STPMIC1_LDO3_MODE;
+               ret &= ~STPMIC1_LDO12356_VOUT_MASK;
+               ret |= STPMIC1_LDO3_1800000;
+               if (buck3_at_1800000v)
+                       ret |= STPMIC1_LDO3_MODE;
+
+               ret = pmic_reg_write(dev, STPMIC1_LDOX_MAIN_CR(STPMIC1_LDO3),
+                                    ret);
+               if (ret < 0)
+                       return ret;
+
+               /* VDD_DDR2 : Set BUCK2 to 1.2V */
+               ret = pmic_clrsetbits(dev,
+                                     STPMIC1_BUCKX_MAIN_CR(STPMIC1_BUCK2),
+                                     STPMIC1_BUCK_VOUT_MASK,
+                                     STPMIC1_BUCK2_1200000V);
+               if (ret < 0)
+                       return ret;
+
+               /* Enable VDD_DDR1 = LDO3 */
+               ret = pmic_clrsetbits(dev, STPMIC1_LDOX_MAIN_CR(STPMIC1_LDO3),
+                                     STPMIC1_LDO_ENA, STPMIC1_LDO_ENA);
+               if (ret < 0)
+                       return ret;
+
+               mdelay(STPMIC1_DEFAULT_START_UP_DELAY_MS);
+
+               /* Enable VDD_DDR2 =BUCK2 */
+               ret = pmic_clrsetbits(dev,
+                                     STPMIC1_BUCKX_MAIN_CR(STPMIC1_BUCK2),
+                                     STPMIC1_BUCK_ENA, STPMIC1_BUCK_ENA);
+               if (ret < 0)
+                       return ret;
+
+               mdelay(STPMIC1_DEFAULT_START_UP_DELAY_MS);
+
+               /* Enable VREF */
+               ret = pmic_clrsetbits(dev, STPMIC1_REFDDR_MAIN_CR,
+                                     STPMIC1_VREF_ENA, STPMIC1_VREF_ENA);
+               if (ret < 0)
+                       return ret;
+
+               mdelay(STPMIC1_DEFAULT_START_UP_DELAY_MS);
+
+               break;
+
+       default:
+               break;
+       };
 
        return 0;
 }
index 1a1951f874ef1bf2122849f32ba2ced84e2dba1b..c759952b80bc1c39770df2a1d98902fd3c586ae0 100644 (file)
@@ -247,6 +247,7 @@ config USE_PREBOOT
 config PREBOOT
        string "preboot default value"
        depends on USE_PREBOOT
+       default ""
        help
          This is the default of "preboot" environment variable.
 
index 8c92feb39991b997aabe95de035d3ae64243a7a0..c7e41ef3073f9a3304a1d52f040203f72560c2d0 100644 (file)
@@ -124,6 +124,7 @@ endif
 
 obj-y += cli.o
 obj-$(CONFIG_FSL_DDR_INTERACTIVE) += cli_simple.o cli_readline.o
+obj-$(CONFIG_STM32MP1_DDR_INTERACTIVE) += cli_simple.o cli_readline.o
 obj-$(CONFIG_DFU_OVER_USB) += dfu.o
 obj-y += command.o
 obj-$(CONFIG_$(SPL_TPL_)LOG) += log.o
index ffcf8cd31dc00f23af72f77cd5960898d71df679..02e14609bb776cdf89d38d8f9753c19e140b30a4 100644 (file)
 STMicroelectronics STM32MP1 clock tree initialization
 =====================================================
 
-The STM32MP clock tree initialization is based on device tree information
-for RCC IP and on fixed clocks.
+The STM32MP1 clock tree initialization is based on device tree information
+for RCC IP node (st,stm32mp1-rcc) and on fixed-clock nodes.
 
--------------------------------
-RCC CLOCK = st,stm32mp1-rcc-clk
--------------------------------
+RCC IP = st,stm32mp1-rcc
+========================
 
 The RCC IP is both a reset and a clock controller but this documentation only
 describes the fields added for clock tree initialization which are not present
-in Linux binding.
+in Linux binding for compatible "st,stm32mp1-rcc" defined in st,stm32mp1-rcc.txt
+file.
 
-Please refer to ../mfd/st,stm32-rcc.txt for all the other properties common
-with Linux.
+The added properties for clock tree initialization are:
 
 Required properties:
+- st,clksrc : The clock sources configuration array in a platform specific
+              order.
 
-- compatible: Should be "st,stm32mp1-rcc-clk"
+  For the STM32MP15x family there are 9 clock sources selector which are
+  configured in the following order:
+       MPU AXI MCU PLL12 PLL3 PLL4 RTC MCO1 MCO2
 
-- st,clksrc : The clock source in this order
+  Clock source configuration values are defined by macros CLK_<NAME>_<SOURCE>
+  from dt-bindings/clock/stm32mp1-clksrc.h.
 
-       for STM32MP15x: 9 clock sources are requested
-               MPU AXI MCU PLL12 PLL3 PLL4 RTC MCO1 MCO2
-
-       with value equals to RCC clock specifier as defined in
-       dt-bindings/clock/stm32mp1-clksrc.h: CLK_<NAME>_<SOURCE>
-
-- st,clkdiv : The div parameters in this order
-       for STM32MP15x: 11 dividers value are requested
+  Example:
+       st,clksrc = <
+               CLK_MPU_PLL1P
+               CLK_AXI_PLL2P
+               CLK_MCU_PLL3P
+               CLK_PLL12_HSE
+               CLK_PLL3_HSE
+               CLK_PLL4_HSE
+               CLK_RTC_LSE
+               CLK_MCO1_DISABLED
+               CLK_MCO2_DISABLED
+       >;
+
+- st,clkdiv : The clock main dividers value specified in an array
+              in a platform specific order.
+
+  When used, it shall describe the whole clock dividers tree.
+
+  For the STM32MP15x family there are 11 dividers values expected.
+  They shall be configured in the following order:
                MPU AXI MCU APB1 APB2 APB3 APB4 APB5 RTC MCO1 MCO2
 
-       with DIV coding defined in RCC associated register RCC_xxxDIVR
-
-       most the case, it is:
+  The each divider value uses the DIV coding defined in RCC associated
+  register RCC_xxxDIVR. In most the case, it is:
                0x0: not divided
                0x1: division by 2
                0x2: division by 4
                0x3: division by 8
                ...
 
-       but for RTC MCO1 MCO2, the coding is different:
+  Note that for RTC MCO1 MCO2, the coding is different:
                0x0: not divided
                0x1: division by 2
                0x2: division by 3
                0x3: division by 4
                ...
 
-Optional Properties:
-- st,pll
-    PLL children node for PLL1 to PLL4 : (see ref manual for details)
-    with associated index 0 to 3 (st,pll@0 to st,pll@4)
-    PLLx is off when the associated node is absent
+  Example:
+       st,clkdiv = <
+               1 /*MPU*/
+               0 /*AXI*/
+               0 /*MCU*/
+               1 /*APB1*/
+               1 /*APB2*/
+               1 /*APB3*/
+               1 /*APB4*/
+               2 /*APB5*/
+               23 /*RTC*/
+               0 /*MCO1*/
+               0 /*MCO2*/
+       >;
 
-    - Sub-nodes:
+Optional Properties:
+- st,pll : A specific PLL configuration, including frequency.
 
-       - cfg:  The parameters for PLL configuration in this order:
-               DIVM DIVN DIVP DIVQ DIVR Output
+  PLL children nodes for PLL1 to PLL4 (see ref manual for details)
+  are listed with associated index 0 to 3 (st,pll@0 to st,pll@3).
+  PLLx is off when the associated node is absent.
 
-               with DIV value as defined in RCC spec:
-                       0x0: bypass (division by 1)
-                       0x1: division by 2
-                       0x2: division by 3
-                       0x3: division by 4
-                       ...
+  Here are the available properties for each PLL node:
 
-               and Output = bitfield for each output value = 1:ON/0:OFF
-                       BIT(0) => output P : DIVPEN
-                       BIT(1) => output Q : DIVQEN
-                       BIT(2) => output R : DIVREN
-                 NB : macro PQR(p,q,r) can be used to build this value
-                      with p,p,r = 0 or 1
+    - cfg: The parameters for PLL configuration in the following order:
+           DIVM DIVN DIVP DIVQ DIVR Output.
 
-       - frac : Fractional part of the multiplication factor
-               (optional, PLL is in integer mode when absent)
+       DIVx values are defined as in RCC spec:
+               0x0: bypass (division by 1)
+               0x1: division by 2
+               0x2: division by 3
+               0x3: division by 4
+               ...
 
-       - csg : Clock Spreading Generator (optional)
-               with parameters in this order:
-               MOD_PER INC_STEP SSCG_MODE
+       Output contains a bitfield for each output value (1:ON/0:OFF)
+               BIT(0) => output P : DIVPEN
+               BIT(1) => output Q : DIVQEN
+               BIT(2) => output R : DIVREN
+         NB: macro PQR(p,q,r) can be used to build this value
+             with p,q,r = 0 or 1.
+
+    - frac : Fractional part of the multiplication factor
+             (optional, PLL is in integer mode when absent).
+
+    - csg : Clock Spreading Generator (optional) with parameters in the
+           following order: MOD_PER INC_STEP SSCG_MODE.
+
+       MOD_PER: Modulation Period Adjustment
+       INC_STEP: Modulation Depth Adjustment
+       SSCG_MODE: Spread spectrum clock generator mode, with associated
+                  defined from stm32mp1-clksrc.h:
+                       - SSCG_MODE_CENTER_SPREAD = 0
+                       - SSCG_MODE_DOWN_SPREAD = 1
+
+    Example:
+       st,pll@0 {
+               cfg = < 1 53 0 0 0 1 >;
+               frac = < 0x810 >;
+       };
+       st,pll@1 {
+               cfg = < 1 43 1 0 0 PQR(0,1,1) >;
+               csg = < 10 20 1 >;
+       };
+       st,pll@2 {
+               cfg = < 2 85 3 13 3 0 >;
+               csg = < 10 20 SSCG_MODE_CENTER_SPREAD >;
+               };
+       st,pll@3 {
+               cfg = < 2 78 4 7 9 3 >;
+       };
 
-               * MOD_PER: Modulation Period Adjustment
-               * INC_STEP: Modulation Depth Adjustment
-               * SSCG_MODE: Spread spectrum clock generator mode
-                 you can use associated defines from stm32mp1-clksrc.h
-                 * SSCG_MODE_CENTER_SPREAD = 0
-                 * SSCG_MODE_DOWN_SPREAD = 1
+- st,pkcs : used to configure the peripherals kernel clock selection.
 
+  The property is a list of peripheral kernel clock source identifiers defined
+  by macros CLK_<KERNEL-CLOCK>_<PARENT-CLOCK> as defined by header file
+  dt-bindings/clock/stm32mp1-clksrc.h.
 
-- st,pkcs : used to configure the peripherals kernel clock selection
-  containing a list of peripheral kernel clock source identifier as defined
-  in the file dt-bindings/clock/stm32mp1-clksrc.h
+  st,pkcs may not list all the kernel clocks and has no ordering requirements.
 
   Example:
+       st,pkcs = <
+               CLK_STGEN_HSE
+               CLK_CKPER_HSI
+               CLK_USBPHY_PLL2P
+               CLK_DSI_PLL2Q
+               CLK_I2C46_HSI
+               CLK_UART1_HSI
+               CLK_UART24_HSI
+       >;
 
-       rcc: rcc@50000000 {
-               compatible = "syscon", "simple-mfd";
-
-               reg = <0x50000000 0x1000>;
-
-               rcc_clk: rcc-clk@50000000 {
-                       #clock-cells = <1>;
-                       compatible = "st,stm32mp1-rcc-clk";
-
-                       st,clksrc = <   CLK_MPU_PLL1P
-                                       CLK_AXI_PLL2P
-                                       CLK_MCU_HSI
-                                       CLK_PLL12_HSE
-                                       CLK_PLL3_HSE
-                                       CLK_PLL4_HSE
-                                       CLK_RTC_HSE
-                                       CLK_MCO1_DISABLED
-                                       CLK_MCO2_DISABLED
-                       >;
-
-                       st,clkdiv = <
-                               1 /*MPU*/
-                               0 /*AXI*/
-                               0 /*MCU*/
-                               1 /*APB1*/
-                               1 /*APB2*/
-                               1 /*APB3*/
-                               1 /*APB4*/
-                               5 /*APB5*/
-                               23 /*RTC*/
-                               0 /*MCO1*/
-                               0 /*MCO2*/
-                       >;
-
-                       st,pll@0 {
-                               cfg = < 1 53 0 0 0 1 >;
-                               frac = < 0x810 >;
-                       };
-                       st,pll@1 {
-                               cfg = < 1 43 1 0 0 PQR(0,1,1) >;
-                               csg = < 10 20 1 >;
-                       };
-                       st,pll@2 {
-                               cfg = < 2 85 3 13 3 0 >;
-                               csg = < 10 20 SSCG_MODE_CENTER_SPREAD >;
-                       };
-                       st,pll@3 {
-                               cfg = < 2 78 4 7 9 3 >;
-                       };
-                       st,pkcs = <
-                                       CLK_STGEN_HSE
-                                       CLK_CKPER_HSI
-                                       CLK_USBPHY_PLL2P
-                                       CLK_DSI_PLL2Q
-                                 >;
-               };
-       };
-
---------------------------
 other clocks = fixed-clock
---------------------------
+==========================
+
 The clock tree is also based on 5 fixed-clock in clocks node
 used to define the state of associated ST32MP1 oscillators:
-- clk-lsi
-- clk-lse
-- clk-hsi
-- clk-hse
-- clk-csi
+  - clk-lsi
+  - clk-lse
+  - clk-hsi
+  - clk-hse
+  - clk-csi
 
 At boot the clock tree initialization will
-- enable the oscillator present in device tree
-- disable HSI oscillator if the node is absent (always activated by bootrom)
+  - enable oscillators present in device tree
+  - disable HSI oscillator if the node is absent (always activated by bootrom)
 
 Optional properties :
 
 a) for external oscillator: "clk-lse", "clk-hse"
 
-       4 optional fields are managed
-       - "st,bypass" Configure the oscillator bypass mode (HSEBYP, LSEBYP)
-       - "st,digbypass" Configure the bypass mode as full-swing digital signal
-         (DIGBYP)
-       - "st,css" Activate the clock security system (HSECSSON, LSECSSON)
-       - "st,drive" (only for LSE) value of the drive for the oscillator
-          (see LSEDRV_ define in the file dt-bindings/clock/stm32mp1-clksrc.h)
-
-       Example board file:
+  4 optional fields are managed
+  - "st,bypass" configures the oscillator bypass mode (HSEBYP, LSEBYP)
+  - "st,digbypass" configures the bypass mode as full-swing digital
+    signal (DIGBYP)
+  - "st,css" activates the clock security system (HSECSSON, LSECSSON)
+  - "st,drive" (only for LSE) contains the value of the drive for the
+     oscillator (see LSEDRV_ defined in the file
+     dt-bindings/clock/stm32mp1-clksrc.h)
 
+  Example board file:
        / {
                clocks {
                        clk_hse: clk-hse {
@@ -200,13 +201,12 @@ a) for external oscillator: "clk-lse", "clk-hse"
 
 b) for internal oscillator: "clk-hsi"
 
-       internally HSI clock is fixed to 64MHz for STM32MP157 soc
-       in device tree clk-hsi is the clock after HSIDIV (ck_hsi in RCC doc)
-       So this clock frequency is used to compute the expected HSI_DIV
-       for the clock tree initialisation
-
-       ex: for HSIDIV = /1
+  Internally HSI clock is fixed to 64MHz for STM32MP157 SoC.
+  In device tree, clk-hsi is the clock after HSIDIV (clk_hsi in RCC
+  doc). So this clock frequency is used to compute the expected HSI_DIV
+  for the clock tree initialization.
 
+  Example with HSIDIV = /1:
        / {
                clocks {
                        clk_hsi: clk-hsi {
@@ -216,8 +216,7 @@ b) for internal oscillator: "clk-hsi"
                        };
        };
 
-       ex: for HSIDIV = /2
-
+  Example with HSIDIV = /2
        / {
                clocks {
                        clk_hsi: clk-hsi {
@@ -226,3 +225,151 @@ b) for internal oscillator: "clk-hsi"
                                clock-frequency = <32000000>;
                        };
        };
+
+Example of clock tree initialization
+====================================
+
+/ {
+       clocks {
+               u-boot,dm-pre-reloc;
+               clk_hse: clk-hse {
+                       u-boot,dm-pre-reloc;
+                       #clock-cells = <0>;
+                       compatible = "fixed-clock";
+                       clock-frequency = <24000000>;
+                       st,digbypass;
+               };
+
+               clk_hsi: clk-hsi {
+                       u-boot,dm-pre-reloc;
+                       #clock-cells = <0>;
+                       compatible = "fixed-clock";
+                       clock-frequency = <64000000>;
+               };
+
+               clk_lse: clk-lse {
+                       u-boot,dm-pre-reloc;
+                       #clock-cells = <0>;
+                       compatible = "fixed-clock";
+                       clock-frequency = <32768>;
+               };
+
+               clk_lsi: clk-lsi {
+                       u-boot,dm-pre-reloc;
+                       #clock-cells = <0>;
+                       compatible = "fixed-clock";
+                       clock-frequency = <32000>;
+               };
+
+               clk_csi: clk-csi {
+                       u-boot,dm-pre-reloc;
+                       #clock-cells = <0>;
+                       compatible = "fixed-clock";
+                       clock-frequency = <4000000>;
+               };
+       };
+
+       soc {
+
+               rcc: rcc@50000000 {
+                       u-boot,dm-pre-reloc;
+                       compatible = "st,stm32mp1-rcc", "syscon";
+                       reg = <0x50000000 0x1000>;
+                       #clock-cells = <1>;
+                       #reset-cells = <1>;
+                       interrupts = <GIC_SPI 5 IRQ_TYPE_LEVEL_HIGH>;
+
+                       st,clksrc = <
+                               CLK_MPU_PLL1P
+                               CLK_AXI_PLL2P
+                               CLK_MCU_PLL3P
+                               CLK_PLL12_HSE
+                               CLK_PLL3_HSE
+                               CLK_PLL4_HSE
+                               CLK_RTC_LSE
+                               CLK_MCO1_DISABLED
+                               CLK_MCO2_DISABLED
+                       >;
+
+                       st,clkdiv = <
+                               1 /*MPU*/
+                               0 /*AXI*/
+                               0 /*MCU*/
+                               1 /*APB1*/
+                               1 /*APB2*/
+                               1 /*APB3*/
+                               1 /*APB4*/
+                               2 /*APB5*/
+                               23 /*RTC*/
+                               0 /*MCO1*/
+                               0 /*MCO2*/
+                       >;
+
+                       st,pkcs = <
+                               CLK_CKPER_HSE
+                               CLK_FMC_ACLK
+                               CLK_QSPI_ACLK
+                               CLK_ETH_DISABLED
+                               CLK_SDMMC12_PLL4P
+                               CLK_DSI_DSIPLL
+                               CLK_STGEN_HSE
+                               CLK_USBPHY_HSE
+                               CLK_SPI2S1_PLL3Q
+                               CLK_SPI2S23_PLL3Q
+                               CLK_SPI45_HSI
+                               CLK_SPI6_HSI
+                               CLK_I2C46_HSI
+                               CLK_SDMMC3_PLL4P
+                               CLK_USBO_USBPHY
+                               CLK_ADC_CKPER
+                               CLK_CEC_LSE
+                               CLK_I2C12_HSI
+                               CLK_I2C35_HSI
+                               CLK_UART1_HSI
+                               CLK_UART24_HSI
+                               CLK_UART35_HSI
+                               CLK_UART6_HSI
+                               CLK_UART78_HSI
+                               CLK_SPDIF_PLL4P
+                               CLK_FDCAN_PLL4Q
+                               CLK_SAI1_PLL3Q
+                               CLK_SAI2_PLL3Q
+                               CLK_SAI3_PLL3Q
+                               CLK_SAI4_PLL3Q
+                               CLK_RNG1_LSI
+                               CLK_RNG2_LSI
+                               CLK_LPTIM1_PCLK1
+                               CLK_LPTIM23_PCLK3
+                               CLK_LPTIM45_LSE
+                       >;
+
+                       /* VCO = 1300.0 MHz => P = 650 (CPU) */
+                       pll1: st,pll@0 {
+                               cfg = < 2 80 0 0 0 PQR(1,0,0) >;
+                               frac = < 0x800 >;
+                               u-boot,dm-pre-reloc;
+                       };
+
+                       /* VCO = 1066.0 MHz => P = 266 (AXI), Q = 533 (GPU),
+                                              R = 533 (DDR) */
+                       pll2: st,pll@1 {
+                               cfg = < 2 65 1 0 0 PQR(1,1,1) >;
+                               frac = < 0x1400 >;
+                               u-boot,dm-pre-reloc;
+                       };
+
+                       /* VCO = 417.8 MHz => P = 209, Q = 24, R = 11 */
+                       pll3: st,pll@2 {
+                               cfg = < 1 33 1 16 36 PQR(1,1,1) >;
+                               frac = < 0x1a04 >;
+                               u-boot,dm-pre-reloc;
+                       };
+
+                       /* VCO = 594.0 MHz => P = 99, Q = 74, R = 74 */
+                       pll4: st,pll@3 {
+                               cfg = < 3 98 5 7 7 PQR(1,1,1) >;
+                               u-boot,dm-pre-reloc;
+                       };
+               };
+       };
+};
index 3028636c451138e42f3cdd956916505e52e7efa5..ee708ce92c785f64d2b65b6e754f5c14d16b6f66 100644 (file)
@@ -16,7 +16,7 @@ included in STM32 Cube tool
 info attributes:
 ----------------
 - st,mem-name  : name for DDR configuration, simple string for information
-- st,mem-speed : DDR expected speed for the setting in MHz
+- st,mem-speed : DDR expected speed for the setting in kHz
 - st,mem-size  : DDR mem size in byte
 
 
@@ -102,7 +102,7 @@ controlleur attributes:
 phyc attributes:
 ----------------
 - st,phy-reg   : phy values depending of the DDR type (DDR3/LPDDR2/LPDDR3)
-       for STM32MP15x: 10 values are requested in this order
+       for STM32MP15x: 11 values are requested in this order
                PGCR
                ACIOCR
                DXCCR
@@ -173,7 +173,7 @@ Example:
                                      "ddrphycapb";
 
                        st,mem-name = "DDR3 2x4Gb 533MHz";
-                       st,mem-speed = <533>;
+                       st,mem-speed = <533000>;
                        st,mem-size = <0x40000000>;
 
                        st,ctl-reg = <
index 24859fd054eec8812726eed3a98472fef97d9a1d..6272b00b9efc7b976d2132f7900977937260789b 100644 (file)
@@ -1448,6 +1448,71 @@ static void pll_csg(struct stm32mp1_clk_priv *priv, int pll_id, u32 *csg)
        setbits_le32(priv->base + pll[pll_id].pllxcr, RCC_PLLNCR_SSCG_CTRL);
 }
 
+static  __maybe_unused int pll_set_rate(struct udevice *dev,
+                                       int pll_id,
+                                       int div_id,
+                                       unsigned long clk_rate)
+{
+       struct stm32mp1_clk_priv *priv = dev_get_priv(dev);
+       unsigned int pllcfg[PLLCFG_NB];
+       ofnode plloff;
+       char name[12];
+       const struct stm32mp1_clk_pll *pll = priv->data->pll;
+       enum stm32mp1_plltype type = pll[pll_id].plltype;
+       int divm, divn, divy;
+       int ret;
+       ulong fck_ref;
+       u32 fracv;
+       u64 value;
+
+       if (div_id > _DIV_NB)
+               return -EINVAL;
+
+       sprintf(name, "st,pll@%d", pll_id);
+       plloff = dev_read_subnode(dev, name);
+       if (!ofnode_valid(plloff))
+               return -FDT_ERR_NOTFOUND;
+
+       ret = ofnode_read_u32_array(plloff, "cfg",
+                                   pllcfg, PLLCFG_NB);
+       if (ret < 0)
+               return -FDT_ERR_NOTFOUND;
+
+       fck_ref = pll_get_fref_ck(priv, pll_id);
+
+       divm = pllcfg[PLLCFG_M];
+       /* select output divider = 0: for _DIV_P, 1:_DIV_Q 2:_DIV_R */
+       divy = pllcfg[PLLCFG_P + div_id];
+
+       /* For: PLL1 & PLL2 => VCO is * 2 but ck_pll_y is also / 2
+        * So same final result than PLL2 et 4
+        * with FRACV
+        * Fck_pll_y = Fck_ref * ((DIVN + 1) + FRACV / 2^13)
+        *             / (DIVy + 1) * (DIVM + 1)
+        * value = (DIVN + 1) * 2^13 + FRACV / 2^13
+        *       = Fck_pll_y (DIVy + 1) * (DIVM + 1) * 2^13 / Fck_ref
+        */
+       value = ((u64)clk_rate * (divy + 1) * (divm + 1)) << 13;
+       value = lldiv(value, fck_ref);
+
+       divn = (value >> 13) - 1;
+       if (divn < DIVN_MIN ||
+           divn > stm32mp1_pll[type].divn_max) {
+               pr_err("divn invalid = %d", divn);
+               return -EINVAL;
+       }
+       fracv = value - ((divn + 1) << 13);
+       pllcfg[PLLCFG_N] = divn;
+
+       /* reconfigure PLL */
+       pll_stop(priv, pll_id);
+       pll_config(priv, pll_id, pllcfg, fracv);
+       pll_start(priv, pll_id);
+       pll_output(priv, pll_id, pllcfg[PLLCFG_O]);
+
+       return 0;
+}
+
 static int set_clksrc(struct stm32mp1_clk_priv *priv, unsigned int clksrc)
 {
        u32 address = priv->base + (clksrc >> 4);
@@ -1820,6 +1885,11 @@ static ulong stm32mp1_clk_set_rate(struct clk *clk, unsigned long clk_rate)
        int p;
 
        switch (clk->id) {
+#if defined(STM32MP1_CLOCK_TREE_INIT) && \
+       defined(CONFIG_STM32MP1_DDR_INTERACTIVE)
+       case DDRPHYC:
+               break;
+#endif
        case LTDC_PX:
        case DSI_PX:
                break;
@@ -1833,6 +1903,19 @@ static ulong stm32mp1_clk_set_rate(struct clk *clk, unsigned long clk_rate)
                return -EINVAL;
 
        switch (p) {
+#if defined(STM32MP1_CLOCK_TREE_INIT) && \
+       defined(CONFIG_STM32MP1_DDR_INTERACTIVE)
+       case _PLL2_R: /* DDRPHYC */
+       {
+               /* only for change DDR clock in interactive mode */
+               ulong result;
+
+               set_clksrc(priv, CLK_AXI_HSI);
+               result = pll_set_rate(clk->dev,  _PLL2, _DIV_R, clk_rate);
+               set_clksrc(priv, CLK_AXI_PLL2P);
+               return result;
+       }
+#endif
        case _PLL4_Q:
                /* for LTDC_PX and DSI_PX case */
                return pll_set_output_rate(clk->dev, _PLL4, _DIV_Q, clk_rate);
index 3872364d6bf59454cc26b5f19fdb0629c2c13c8d..50c4fd0de23ab7e8d54ab918a65336a2a56adc99 100644 (file)
@@ -500,7 +500,7 @@ static int stm32_i2c_compute_solutions(struct stm32_i2c_setup *setup,
        af_delay_max = setup->analog_filter ?
                       STM32_I2C_ANALOG_FILTER_DELAY_MAX : 0;
 
-       sdadel_min = setup->fall_time - i2c_specs[setup->speed].hddat_min -
+       sdadel_min = i2c_specs[setup->speed].hddat_min + setup->fall_time -
                     af_delay_min - (setup->dnf + 3) * i2cclk;
 
        sdadel_max = i2c_specs[setup->speed].vddat_max - setup->rise_time -
@@ -540,8 +540,12 @@ static int stm32_i2c_compute_solutions(struct stm32_i2c_setup *setup,
                                        p_prev = p;
 
                                        list_add_tail(&v->node, solutions);
+                                       break;
                                }
                        }
+
+                       if (p_prev == p)
+                               break;
                }
        }
 
index b9c816662c4c9672e5f479f73db6a35b36a6a762..2fd8c7b7e3b89a10f3ac6eedc1bd66a35cc4329b 100644 (file)
@@ -10,3 +10,40 @@ config STM32MP1_DDR
                family: support for LPDDR2, LPDDR3 and DDR3
                the SDRAM parameters for controleur and phy need to be provided
                in device tree (computed by DDR tuning tools)
+
+config STM32MP1_DDR_INTERACTIVE
+       bool "STM32MP1 DDR driver : interactive support"
+       depends on STM32MP1_DDR
+       help
+               activate interactive support in STM32MP1 DDR controller driver
+               used for DDR tuning tools
+               to enter in intercative mode type 'd' during SPL DDR driver
+               initialisation
+
+config STM32MP1_DDR_INTERACTIVE_FORCE
+       bool "STM32MP1 DDR driver : force interactive mode"
+       depends on STM32MP1_DDR_INTERACTIVE
+       default n
+       help
+               force interactive mode in STM32MP1 DDR controller driver
+               skip the polling of character 'd' in console
+               useful when SPL is loaded in sysram
+               directly by programmer
+
+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
index 79eb028fab6c625298812f86155cd591a536953b..e1e9135603a06495601b296ab5c1368954627bc6 100644 (file)
@@ -5,3 +5,11 @@
 
 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
index c7c3ba70a4ec95ebb47b0bc4cfba79e32727a617..d765a46f7c21f9b8524d24567a2a5c94f2e97b0a 100644 (file)
@@ -41,8 +41,32 @@ struct reg_desc {
         offsetof(struct stm32mp1_ddrphy, x),\
         offsetof(struct y, x)}
 
+#define DDR_REG_DYN(x) \
+       {#x,\
+        offsetof(struct stm32mp1_ddrctl, x),\
+        INVALID_OFFSET}
+
+#define DDRPHY_REG_DYN(x) \
+       {#x,\
+        offsetof(struct stm32mp1_ddrphy, x),\
+        INVALID_OFFSET}
+
+/***********************************************************
+ * PARAMETERS: value get from device tree :
+ *             size / order need to be aligned with binding
+ *             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[] = {
+static const struct reg_desc ddr_reg[DDRCTL_REG_REG_SIZE] = {
        DDRCTL_REG_REG(mstr),
        DDRCTL_REG_REG(mrctrl0),
        DDRCTL_REG_REG(mrctrl1),
@@ -71,7 +95,7 @@ static const struct reg_desc ddr_reg[] = {
 };
 
 #define DDRCTL_REG_TIMING(x)   DDRCTL_REG(x, stm32mp1_ddrctrl_timing)
-static const struct reg_desc ddr_timing[] = {
+static const struct reg_desc ddr_timing[DDRCTL_REG_TIMING_SIZE] = {
        DDRCTL_REG_TIMING(rfshtmg),
        DDRCTL_REG_TIMING(dramtmg0),
        DDRCTL_REG_TIMING(dramtmg1),
@@ -87,7 +111,7 @@ static const struct reg_desc ddr_timing[] = {
 };
 
 #define DDRCTL_REG_MAP(x)      DDRCTL_REG(x, stm32mp1_ddrctrl_map)
-static const struct reg_desc ddr_map[] = {
+static const struct reg_desc ddr_map[DDRCTL_REG_MAP_SIZE] = {
        DDRCTL_REG_MAP(addrmap1),
        DDRCTL_REG_MAP(addrmap2),
        DDRCTL_REG_MAP(addrmap3),
@@ -100,7 +124,7 @@ static const struct reg_desc ddr_map[] = {
 };
 
 #define DDRCTL_REG_PERF(x)     DDRCTL_REG(x, stm32mp1_ddrctrl_perf)
-static const struct reg_desc ddr_perf[] = {
+static const struct reg_desc ddr_perf[DDRCTL_REG_PERF_SIZE] = {
        DDRCTL_REG_PERF(sched),
        DDRCTL_REG_PERF(sched1),
        DDRCTL_REG_PERF(perfhpr1),
@@ -121,7 +145,7 @@ static const struct reg_desc ddr_perf[] = {
 };
 
 #define DDRPHY_REG_REG(x)      DDRPHY_REG(x, stm32mp1_ddrphy_reg)
-static const struct reg_desc 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),
@@ -136,7 +160,7 @@ static const struct reg_desc ddrphy_reg[] = {
 };
 
 #define DDRPHY_REG_TIMING(x)   DDRPHY_REG(x, stm32mp1_ddrphy_timing)
-static const struct reg_desc 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),
@@ -150,7 +174,7 @@ static const struct reg_desc ddrphy_timing[] = {
 };
 
 #define DDRPHY_REG_CAL(x)      DDRPHY_REG(x, stm32mp1_ddrphy_cal)
-static const struct reg_desc 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),
@@ -165,6 +189,45 @@ static const struct reg_desc ddrphy_cal[] = {
        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,
@@ -173,6 +236,13 @@ enum reg_type {
        REGPHY_REG,
        REGPHY_TIMING,
        REGPHY_CAL,
+#ifdef CONFIG_STM32MP1_DDR_INTERACTIVE
+/* dynamic registers => managed in driver or not changed,
+ * can be dumped in interactive mode
+ */
+       REG_DYN,
+       REGPHY_DYN,
+#endif
        REG_TYPE_NB
 };
 
@@ -193,19 +263,26 @@ struct ddr_reg_info {
 
 const struct ddr_reg_info ddr_registers[REG_TYPE_NB] = {
 [REG_REG] = {
-       "static", ddr_reg, ARRAY_SIZE(ddr_reg), DDR_BASE},
+       "static", ddr_reg, DDRCTL_REG_REG_SIZE, DDR_BASE},
 [REG_TIMING] = {
-       "timing", ddr_timing, ARRAY_SIZE(ddr_timing), DDR_BASE},
+       "timing", ddr_timing, DDRCTL_REG_TIMING_SIZE, DDR_BASE},
 [REG_PERF] = {
-       "perf", ddr_perf, ARRAY_SIZE(ddr_perf), DDR_BASE},
+       "perf", ddr_perf, DDRCTL_REG_PERF_SIZE, DDR_BASE},
 [REG_MAP] = {
-       "map", ddr_map, ARRAY_SIZE(ddr_map), DDR_BASE},
+       "map", ddr_map, DDRCTL_REG_MAP_SIZE, DDR_BASE},
 [REGPHY_REG] = {
-       "static", ddrphy_reg, ARRAY_SIZE(ddrphy_reg), DDRPHY_BASE},
+       "static", ddrphy_reg, DDRPHY_REG_REG_SIZE, DDRPHY_BASE},
 [REGPHY_TIMING] = {
-       "timing", ddrphy_timing, ARRAY_SIZE(ddrphy_timing), DDRPHY_BASE},
+       "timing", ddrphy_timing, DDRPHY_REG_TIMING_SIZE, DDRPHY_BASE},
 [REGPHY_CAL] = {
-       "cal", ddrphy_cal, ARRAY_SIZE(ddrphy_cal), DDRPHY_BASE},
+       "cal", ddrphy_cal, DDRPHY_REG_CAL_SIZE, DDRPHY_BASE},
+#ifdef CONFIG_STM32MP1_DDR_INTERACTIVE
+[REG_DYN] = {
+       "dyn", ddr_dyn, DDR_REG_DYN_SIZE, DDR_BASE},
+[REGPHY_DYN] = {
+       "dyn", ddrphy_dyn, DDRPHY_REG_DYN_SIZE, DDRPHY_BASE},
+#endif
+
 };
 
 const char *base_name[] = {
@@ -246,6 +323,231 @@ static void set_reg(const struct ddr_info *priv,
        }
 }
 
+#ifdef CONFIG_STM32MP1_DDR_INTERACTIVE
+static void stm32mp1_dump_reg_desc(u32 base_addr, const struct reg_desc *desc)
+{
+       unsigned int *ptr;
+
+       ptr = (unsigned int *)(base_addr + desc->offset);
+       printf("%s= 0x%08x\n", desc->name, readl(ptr));
+}
+
+static void stm32mp1_dump_param_desc(u32 par_addr, const struct reg_desc *desc)
+{
+       unsigned int *ptr;
+
+       ptr = (unsigned int *)(par_addr + desc->par_offset);
+       printf("%s= 0x%08x\n", desc->name, readl(ptr));
+}
+
+static const struct reg_desc *found_reg(const char *name, enum reg_type *type)
+{
+       unsigned int i, j;
+       const struct reg_desc *desc;
+
+       for (i = 0; i < ARRAY_SIZE(ddr_registers); i++) {
+               desc = ddr_registers[i].desc;
+               for (j = 0; j < ddr_registers[i].size; j++) {
+                       if (strcmp(name, desc[j].name) == 0) {
+                               *type = i;
+                               return &desc[j];
+                       }
+               }
+       }
+       *type = REG_TYPE_NB;
+       return NULL;
+}
+
+int stm32mp1_dump_reg(const struct ddr_info *priv,
+                     const char *name)
+{
+       unsigned int i, j;
+       const struct reg_desc *desc;
+       u32 base_addr;
+       enum base_type p_base;
+       enum reg_type type;
+       const char *p_name;
+       enum base_type filter = NONE_BASE;
+       int result = -1;
+
+       if (name) {
+               if (strcmp(name, base_name[DDR_BASE]) == 0)
+                       filter = DDR_BASE;
+               else if (strcmp(name, base_name[DDRPHY_BASE]) == 0)
+                       filter = DDRPHY_BASE;
+       }
+
+       for (i = 0; i < ARRAY_SIZE(ddr_registers); i++) {
+               p_base = ddr_registers[i].base;
+               p_name = ddr_registers[i].name;
+               if (!name || (filter == p_base || !strcmp(name, p_name))) {
+                       result = 0;
+                       desc = ddr_registers[i].desc;
+                       base_addr = get_base_addr(priv, p_base);
+                       printf("==%s.%s==\n", base_name[p_base], p_name);
+                       for (j = 0; j < ddr_registers[i].size; j++)
+                               stm32mp1_dump_reg_desc(base_addr, &desc[j]);
+               }
+       }
+       if (result) {
+               desc = found_reg(name, &type);
+               if (desc) {
+                       p_base = ddr_registers[type].base;
+                       base_addr = get_base_addr(priv, p_base);
+                       stm32mp1_dump_reg_desc(base_addr, desc);
+                       result = 0;
+               }
+       }
+       return result;
+}
+
+void stm32mp1_edit_reg(const struct ddr_info *priv,
+                      char *name, char *string)
+{
+       unsigned long *ptr, value;
+       enum reg_type type;
+       enum base_type base;
+       const struct reg_desc *desc;
+       u32 base_addr;
+
+       desc = found_reg(name, &type);
+
+       if (!desc) {
+               printf("%s not found\n", name);
+               return;
+       }
+       if (strict_strtoul(string, 16, &value) < 0) {
+               printf("invalid value %s\n", string);
+               return;
+       }
+       base = ddr_registers[type].base;
+       base_addr = get_base_addr(priv, base);
+       ptr = (unsigned long *)(base_addr + desc->offset);
+       writel(value, ptr);
+       printf("%s= 0x%08x\n", desc->name, readl(ptr));
+}
+
+static u32 get_par_addr(const struct stm32mp1_ddr_config *config,
+                       enum reg_type type)
+{
+       u32 par_addr = 0x0;
+
+       switch (type) {
+       case REG_REG:
+               par_addr = (u32)&config->c_reg;
+               break;
+       case REG_TIMING:
+               par_addr = (u32)&config->c_timing;
+               break;
+       case REG_PERF:
+               par_addr = (u32)&config->c_perf;
+               break;
+       case REG_MAP:
+               par_addr = (u32)&config->c_map;
+               break;
+       case REGPHY_REG:
+               par_addr = (u32)&config->p_reg;
+               break;
+       case REGPHY_TIMING:
+               par_addr = (u32)&config->p_timing;
+               break;
+       case REGPHY_CAL:
+               par_addr = (u32)&config->p_cal;
+               break;
+       case REG_DYN:
+       case REGPHY_DYN:
+       case REG_TYPE_NB:
+               par_addr = (u32)NULL;
+               break;
+       }
+
+       return par_addr;
+}
+
+int stm32mp1_dump_param(const struct stm32mp1_ddr_config *config,
+                       const char *name)
+{
+       unsigned int i, j;
+       const struct reg_desc *desc;
+       u32 par_addr;
+       enum base_type p_base;
+       enum reg_type type;
+       const char *p_name;
+       enum base_type filter = NONE_BASE;
+       int result = -EINVAL;
+
+       if (name) {
+               if (strcmp(name, base_name[DDR_BASE]) == 0)
+                       filter = DDR_BASE;
+               else if (strcmp(name, base_name[DDRPHY_BASE]) == 0)
+                       filter = DDRPHY_BASE;
+       }
+
+       for (i = 0; i < ARRAY_SIZE(ddr_registers); i++) {
+               par_addr = get_par_addr(config, i);
+               if (!par_addr)
+                       continue;
+               p_base = ddr_registers[i].base;
+               p_name = ddr_registers[i].name;
+               if (!name || (filter == p_base || !strcmp(name, p_name))) {
+                       result = 0;
+                       desc = ddr_registers[i].desc;
+                       printf("==%s.%s==\n", base_name[p_base], p_name);
+                       for (j = 0; j < ddr_registers[i].size; j++)
+                               stm32mp1_dump_param_desc(par_addr, &desc[j]);
+               }
+       }
+       if (result) {
+               desc = found_reg(name, &type);
+               if (desc) {
+                       par_addr = get_par_addr(config, type);
+                       if (par_addr) {
+                               stm32mp1_dump_param_desc(par_addr, desc);
+                               result = 0;
+                       }
+               }
+       }
+       return result;
+}
+
+void stm32mp1_edit_param(const struct stm32mp1_ddr_config *config,
+                        char *name, char *string)
+{
+       unsigned long *ptr, value;
+       enum reg_type type;
+       const struct reg_desc *desc;
+       u32 par_addr;
+
+       desc = found_reg(name, &type);
+       if (!desc) {
+               printf("%s not found\n", name);
+               return;
+       }
+       if (strict_strtoul(string, 16, &value) < 0) {
+               printf("invalid value %s\n", string);
+               return;
+       }
+       par_addr = get_par_addr(config, type);
+       if (!par_addr) {
+               printf("no parameter %s\n", name);
+               return;
+       }
+       ptr = (unsigned long *)(par_addr + desc->par_offset);
+       writel(value, ptr);
+       printf("%s= 0x%08x\n", desc->name, readl(ptr));
+}
+#endif
+
+__weak bool stm32mp1_ddr_interactive(void *priv,
+                                    enum stm32mp1_ddr_interact_step step,
+                                    const struct stm32mp1_ddr_config *config)
+{
+       return false;
+}
+
+#define INTERACTIVE(step)\
+       stm32mp1_ddr_interactive(priv, step, config)
+
 static void ddrphy_idone_wait(struct stm32mp1_ddrphy *phy)
 {
        u32 pgsr;
@@ -312,7 +614,7 @@ static void wait_operating_mode(struct ddr_info *priv, int mode)
        /* self-refresh due to software => check also STAT.selfref_type */
        if (mode == DDRCTRL_STAT_OPERATING_MODE_SR) {
                mask |= DDRCTRL_STAT_SELFREF_TYPE_MASK;
-               stat |= DDRCTRL_STAT_SELFREF_TYPE_SR;
+               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 |
@@ -355,7 +657,7 @@ void stm32mp1_refresh_restore(struct stm32mp1_ddrctl *ctl,
 }
 
 /* board-specific DDR power initializations. */
-__weak int board_ddr_power_init(void)
+__weak int board_ddr_power_init(enum ddr_type ddr_type)
 {
        return 0;
 }
@@ -365,15 +667,21 @@ void stm32mp1_ddr_init(struct ddr_info *priv,
                       const struct stm32mp1_ddr_config *config)
 {
        u32 pir;
-       int ret;
+       int ret = -EINVAL;
 
-       ret = board_ddr_power_init();
+       if (config->c_reg.mstr & DDRCTRL_MSTR_DDR3)
+               ret = board_ddr_power_init(STM32MP_DDR3);
+       else if (config->c_reg.mstr & DDRCTRL_MSTR_LPDDR2)
+               ret = board_ddr_power_init(STM32MP_LPDDR2);
+       else if (config->c_reg.mstr & DDRCTRL_MSTR_LPDDR3)
+               ret = board_ddr_power_init(STM32MP_LPDDR3);
 
        if (ret)
                panic("ddr power init failed\n");
 
+start:
        debug("name = %s\n", config->info.name);
-       debug("speed = %d MHz\n", config->info.speed);
+       debug("speed = %d kHz\n", config->info.speed);
        debug("size  = 0x%x\n", config->info.size);
 /*
  * 1. Program the DWC_ddr_umctl2 registers
@@ -389,7 +697,7 @@ void stm32mp1_ddr_init(struct ddr_info *priv,
 
 /* 1.2. start CLOCK */
        if (stm32mp1_ddr_clk_enable(priv, config->info.speed))
-               panic("invalid DRAM clock : %d MHz\n",
+               panic("invalid DRAM clock : %d kHz\n",
                      config->info.speed);
 
 /* 1.3. deassert reset */
@@ -401,11 +709,12 @@ void stm32mp1_ddr_init(struct ddr_info *priv,
         */
        clrbits_le32(priv->rcc + RCC_DDRITFCR, RCC_DDRITFCR_DDRCAPBRST);
 
-/* 1.4. wait 4 cycles for synchronization */
-       asm(" nop");
-       asm(" nop");
-       asm(" nop");
-       asm(" nop");
+/* 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 */
@@ -424,6 +733,9 @@ void stm32mp1_ddr_init(struct ddr_info *priv,
 
        set_reg(priv, REG_PERF, &config->c_perf);
 
+       if (INTERACTIVE(STEP_CTL_INIT))
+               goto start;
+
 /*  2. deassert reset signal core_ddrc_rstn, aresetn and presetn */
        clrbits_le32(priv->rcc + RCC_DDRITFCR, RCC_DDRITFCR_DDRCORERST);
        clrbits_le32(priv->rcc + RCC_DDRITFCR, RCC_DDRITFCR_DDRCAXIRST);
@@ -436,6 +748,9 @@ void stm32mp1_ddr_init(struct ddr_info *priv,
        set_reg(priv, REGPHY_TIMING, &config->p_timing);
        set_reg(priv, REGPHY_CAL, &config->p_cal);
 
+       if (INTERACTIVE(STEP_PHY_INIT))
+               goto start;
+
 /*  4. Monitor PHY init status by polling PUBL register PGSR.IDONE
  *     Perform DDR PHY DRAM initialization and Gate Training Evaluation
  */
@@ -492,4 +807,7 @@ void stm32mp1_ddr_init(struct ddr_info *priv,
        /* enable uMCTL2 AXI port 0 and 1 */
        setbits_le32(&priv->ctl->pctrl_0, DDRCTRL_PCTRL_N_PORT_EN);
        setbits_le32(&priv->ctl->pctrl_1, DDRCTRL_PCTRL_N_PORT_EN);
+
+       if (INTERACTIVE(STEP_DDR_READY))
+               goto start;
 }
index 3cd0161299eb487e1bab28abaeecfa347af3388a..a8eed89e3ccb9f556c6bf36d3b1bac2b6430602c 100644 (file)
@@ -157,7 +157,7 @@ struct stm32mp1_ddrphy_cal {
 
 struct stm32mp1_ddr_info {
        const char *name;
-       u16 speed; /* in MHZ */
+       u32 speed; /* in kHZ */
        u32 size;  /* memory size in byte = col * row * width */
 };
 
@@ -172,7 +172,7 @@ struct stm32mp1_ddr_config {
        struct stm32mp1_ddrphy_cal p_cal;
 };
 
-int stm32mp1_ddr_clk_enable(struct ddr_info *priv, u16 mem_speed);
+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,
index a606b2bcbe27dc9d8ef9da2eb71ebee2be0625f0..9d33186b3a25cc7df109db3339d719b21f715b5d 100644 (file)
@@ -234,6 +234,8 @@ struct stm32mp1_ddrphy {
 
 /* 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)
@@ -330,6 +332,7 @@ struct stm32mp1_ddrphy {
 
 #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
diff --git a/drivers/ram/stm32mp1/stm32mp1_interactive.c b/drivers/ram/stm32mp1/stm32mp1_interactive.c
new file mode 100644 (file)
index 0000000..cc9b2e7
--- /dev/null
@@ -0,0 +1,483 @@
+// SPDX-License-Identifier: GPL-2.0+ OR BSD-3-Clause
+/*
+ * Copyright (C) 2019, STMicroelectronics - All Rights Reserved
+ */
+
+#include <common.h>
+#include <console.h>
+#include <cli.h>
+#include <clk.h>
+#include <malloc.h>
+#include <ram.h>
+#include <reset.h>
+#include "stm32mp1_ddr.h"
+#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 or speed\n"
+               "freq                       displays the DDR PHY frequency in kHz\n"
+               "freq  <freq>               changes the DDR PHY frequency\n"
+               "param [type|reg]           prints input parameters\n"
+               "param <reg> <val>          edits parameters in step 0\n"
+               "print [type|reg]           dumps registers\n"
+               "edit <reg> <val>           modifies one register\n"
+               "step                       lists the available step\n"
+               "step <n>                   go to the step <n>\n"
+               "next                       goes to the next step\n"
+               "go                         continues the U-Boot SPL execution\n"
+               "reset                      reboots machine\n"
+#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);
+               return;
+       }
+
+       if (argc < 3) {
+               printf("no enought parameter\n");
+               return;
+       }
+       if (!strcmp(argv[1], "name")) {
+               u32 i, name_len = 0;
+
+               for (i = 2; i < argc; i++)
+                       name_len += strlen(argv[i]) + 1;
+               if (ddr_name)
+                       free(ddr_name);
+               ddr_name = malloc(name_len);
+               config->info.name = ddr_name;
+               if (!ddr_name) {
+                       printf("alloc error, length %d\n", name_len);
+                       return;
+               }
+               strcpy(ddr_name, argv[2]);
+               for (i = 3; i < argc; i++) {
+                       strcat(ddr_name, " ");
+                       strcat(ddr_name, argv[i]);
+               }
+               printf("name = %s\n", ddr_name);
+               return;
+       }
+       if (!strcmp(argv[1], "size")) {
+               if (strict_strtoul(argv[2], 16, &value) < 0) {
+                       printf("invalid value %s\n", argv[2]);
+               } else {
+                       config->info.size = value;
+                       printf("size = 0x%x\n", config->info.size);
+               }
+               return;
+       }
+       if (!strcmp(argv[1], "speed")) {
+               if (strict_strtoul(argv[2], 10, &value) < 0) {
+                       printf("invalid value %s\n", argv[2]);
+               } else {
+                       config->info.speed = value;
+                       printf("speed = %d kHz\n", config->info.speed);
+                       value = clk_get_rate(&priv->clk);
+                       printf("DDRPHY = %ld kHz\n", value / 1000);
+               }
+               return;
+       }
+       printf("argument %s invalid\n", argv[1]);
+}
+
+static bool stm32mp1_do_freq(struct ddr_info *priv,
+                            int argc, char * const argv[])
+{
+       unsigned long ddrphy_clk;
+
+       if (argc == 2) {
+               if (strict_strtoul(argv[1], 0, &ddrphy_clk) < 0) {
+                       printf("invalid argument %s", argv[1]);
+                       return false;
+               }
+               if (clk_set_rate(&priv->clk, ddrphy_clk * 1000)) {
+                       printf("ERROR: update failed!\n");
+                       return false;
+               }
+       }
+       ddrphy_clk = clk_get_rate(&priv->clk);
+       printf("DDRPHY = %ld kHz\n", ddrphy_clk / 1000);
+       if (argc == 2)
+               return true;
+       return false;
+}
+
+static void stm32mp1_do_param(enum stm32mp1_ddr_interact_step step,
+                             const struct stm32mp1_ddr_config *config,
+                             int argc, char * const argv[])
+{
+       switch (argc) {
+       case 1:
+               stm32mp1_dump_param(config, NULL);
+               break;
+       case 2:
+               if (stm32mp1_dump_param(config, argv[1]))
+                       printf("invalid argument %s\n",
+                              argv[1]);
+               break;
+       case 3:
+               if (!stm32mp1_check_step(step, STEP_DDR_RESET))
+                       return;
+               stm32mp1_edit_param(config, argv[1], argv[2]);
+               break;
+       }
+}
+
+static void stm32mp1_do_print(struct ddr_info *priv,
+                             int argc, char * const argv[])
+{
+       switch (argc) {
+       case 1:
+               stm32mp1_dump_reg(priv, NULL);
+               break;
+       case 2:
+               if (stm32mp1_dump_reg(priv, argv[1]))
+                       printf("invalid argument %s\n",
+                              argv[1]);
+               break;
+       }
+}
+
+static int stm32mp1_do_step(enum stm32mp1_ddr_interact_step step,
+                           int argc, char * const argv[])
+{
+       int i;
+       unsigned long value;
+
+       switch (argc) {
+       case 1:
+               for (i = 0; i < ARRAY_SIZE(step_str); i++)
+                       printf("%d:%s\n", i, step_str[i]);
+               break;
+
+       case 2:
+               if ((strict_strtoul(argv[1], 0,
+                                   &value) < 0) ||
+                                   value >= ARRAY_SIZE(step_str)) {
+                       printf("invalid argument %s\n",
+                              argv[1]);
+                       goto end;
+               }
+
+               if (value != STEP_DDR_RESET &&
+                   value <= step) {
+                       printf("invalid target %d:%s, current step is %d:%s\n",
+                              (int)value, step_str[value],
+                              step, step_str[step]);
+                       goto end;
+               }
+               printf("step to %d:%s\n",
+                      (int)value, step_str[value]);
+               return (int)value;
+       };
+
+end:
+       return step;
+}
+
+#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)
+{
+       const char *prompt = "DDR>";
+       char buffer[CONFIG_SYS_CBSIZE];
+       char *argv[CONFIG_SYS_MAXARGS + 1];     /* NULL terminated */
+       int argc;
+       static int next_step = -1;
+
+       if (next_step < 0 && step == STEP_DDR_RESET) {
+#ifdef CONFIG_STM32MP1_DDR_INTERACTIVE_FORCE
+               gd->flags &= ~(GD_FLG_SILENT |
+                              GD_FLG_DISABLE_CONSOLE);
+               next_step = STEP_DDR_RESET;
+#else
+               unsigned long start = get_timer(0);
+
+               while (1) {
+                       if (tstc() && (getc() == 'd')) {
+                               next_step = STEP_DDR_RESET;
+                               break;
+                       }
+                       if (get_timer(start) > 100)
+                               break;
+               }
+#endif
+       }
+
+       debug("** step %d ** %s / %d\n", step, step_str[step], next_step);
+
+       if (next_step < 0)
+               return false;
+
+       if (step < 0 || step > ARRAY_SIZE(step_str)) {
+               printf("** step %d ** INVALID\n", step);
+               return false;
+       }
+
+       printf("%d:%s\n", step, step_str[step]);
+       printf("%s\n", prompt);
+
+       if (next_step > step)
+               return false;
+
+       while (next_step == step) {
+               cli_readline_into_buffer(prompt, buffer, 0);
+               argc = cli_simple_parse_line(buffer, argv);
+               if (!argc)
+                       continue;
+
+               switch (stm32mp1_get_command(argv[0], argc)) {
+               case DDR_CMD_HELP:
+                       stm32mp1_do_usage();
+                       break;
+
+               case DDR_CMD_INFO:
+                       stm32mp1_do_info(priv,
+                                        (struct stm32mp1_ddr_config *)config,
+                                        step, argc, argv);
+                       break;
+
+               case DDR_CMD_FREQ:
+                       if (stm32mp1_do_freq(priv, argc, argv))
+                               next_step = STEP_DDR_RESET;
+                       break;
+
+               case DDR_CMD_RESET:
+                       do_reset(NULL, 0, 0, NULL);
+                       break;
+
+               case DDR_CMD_PARAM:
+                       stm32mp1_do_param(step, config, argc, argv);
+                       break;
+
+               case DDR_CMD_PRINT:
+                       stm32mp1_do_print(priv, argc, argv);
+                       break;
+
+               case DDR_CMD_EDIT:
+                       stm32mp1_edit_reg(priv, argv[1], argv[2]);
+                       break;
+
+               case DDR_CMD_GO:
+                       next_step = STEP_RUN;
+                       break;
+
+               case DDR_CMD_NEXT:
+                       next_step = step + 1;
+                       break;
+
+               case DDR_CMD_STEP:
+                       next_step = stm32mp1_do_step(step, argc, argv);
+                       break;
+
+#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;
+}
index e45a3b2658a31dce190c436fb659b7db278244fb..84e39d093b552d19a9f8cf43dc23bcb5faa77eeb 100644 (file)
@@ -20,7 +20,7 @@ static const char *const clkname[] = {
        "ddrphyc" /* LAST clock => used for get_rate() */
 };
 
-int stm32mp1_ddr_clk_enable(struct ddr_info *priv, uint16_t mem_speed)
+int stm32mp1_ddr_clk_enable(struct ddr_info *priv, uint32_t mem_speed)
 {
        unsigned long ddrphy_clk;
        unsigned long ddr_clk;
@@ -43,13 +43,13 @@ int stm32mp1_ddr_clk_enable(struct ddr_info *priv, uint16_t mem_speed)
        priv->clk = clk;
        ddrphy_clk = clk_get_rate(&priv->clk);
 
-       debug("DDR: mem_speed (%d MHz), RCC %d MHz\n",
-             mem_speed, (u32)(ddrphy_clk / 1000 / 1000));
+       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 * 1000);
-       if (ddr_clk > (mem_speed * 1000 * 100)) {
-               pr_err("DDR expected freq %d MHz, current is %d MHz\n",
-                      mem_speed, (u32)(ddrphy_clk / 1000 / 1000));
+       ddr_clk = abs(ddrphy_clk - mem_speed * 1000);
+       if (ddr_clk > (mem_speed * 100)) {
+               pr_err("DDR expected freq %d kHz, current is %d kHz\n",
+                      mem_speed, (u32)(ddrphy_clk / 1000));
                return -EINVAL;
        }
 
@@ -102,8 +102,8 @@ static __maybe_unused int stm32mp1_ddr_setup(struct udevice *dev)
                debug("%s: %s[0x%x] = %d\n", __func__,
                      param[idx].name, param[idx].size, ret);
                if (ret) {
-                       pr_err("%s: Cannot read %s\n",
-                              __func__, param[idx].name);
+                       pr_err("%s: Cannot read %s, error=%d\n",
+                              __func__, param[idx].name, ret);
                        return -EINVAL;
                }
        }
diff --git a/drivers/ram/stm32mp1/stm32mp1_tests.c b/drivers/ram/stm32mp1/stm32mp1_tests.c
new file mode 100644 (file)
index 0000000..b6fb2a9
--- /dev/null
@@ -0,0 +1,1426 @@
+// SPDX-License-Identifier: GPL-2.0+ OR BSD-3-Clause
+/*
+ * Copyright (C) 2019, STMicroelectronics - All Rights Reserved
+ */
+#include <common.h>
+#include <console.h>
+#include <asm/io.h>
+#include <linux/log2.h>
+#include "stm32mp1_tests.h"
+
+#define ADDR_INVALID   0xFFFFFFFF
+
+DECLARE_GLOBAL_DATA_PTR;
+
+static int get_bufsize(char *string, int argc, char *argv[], int arg_nb,
+                      size_t *bufsize, size_t default_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 == 0) {
+                       sprintf(string, "invalid size %s", argv[arg_nb]);
+                       return -1;
+               }
+               if (value & 0x3) {
+                       sprintf(string, "unaligned size %s",
+                               argv[arg_nb]);
+                       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 (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);
+       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);
+               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]);
+               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;
+                               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;
+                               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, 4 * 1024))
+               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;
+
+       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))
+               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))
+               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))
+               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%x\n", nb_loop, addr);
+       while (!error) {
+               seed = rand();
+               for (offset = addr; offset < addr + bufsize; offset += 4)
+                       writel(rand(), offset);
+
+               memcpy((void *)addr + bufsize, (void *)addr, bufsize);
+
+               srand(seed);
+               for (offset = addr; offset < addr + 2 * bufsize; offset += 4) {
+                       if (offset == (addr + bufsize))
+                               srand(seed);
+                       value = rand();
+                       error = check_addr(offset, value);
+                       if (error)
+                               break;
+                       if (progress(offset))
+                               return TEST_FAILED;
+               }
+               if (test_loop_end(&loop, nb_loop, 100))
+                       break;
+       }
+
+       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))
+               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 = 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, #8");
+               __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;
+
+       if (get_bufsize(string, argc, argv, 0, &bufsize, 4 * 1024))
+               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",
+              STM32_DDR_BASE, 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 *)STM32_DDR_BASE,
+                                       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))
+               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))
+               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))
+               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))
+               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))
+               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))
+               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;
+       bool random = false;
+
+       if (get_addr(string, argc, argv, 0, (u32 *)&addr))
+               return TEST_ERROR;
+
+       if ((u32)addr == ADDR_INVALID) {
+               printf("random ");
+               random = true;
+       }
+
+       printf("running at 0x%08x\n", (u32)addr);
+
+       while (1) {
+               if (random)
+                       addr = (u32 *)(STM32_DDR_BASE +
+                              (rand() & (STM32_DDR_SIZE - 1) & ~0x3));
+               data = readl(addr);
+               if (test_loop_end(&loop, 0, 1000))
+                       break;
+       }
+       sprintf(string, "0x%x: %x", (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 = 0xA5A5AA55;
+       u32 loop = 0;
+       bool random = false;
+
+       if (get_addr(string, argc, argv, 0, (u32 *)&addr))
+               return TEST_ERROR;
+
+       if ((u32)addr == ADDR_INVALID) {
+               printf("random ");
+               random = true;
+       }
+
+       printf("running at 0x%08x\n", (u32)addr);
+
+       while (1) {
+               if (random) {
+                       addr = (u32 *)(STM32_DDR_BASE +
+                              (rand() & (STM32_DDR_SIZE - 1) & ~0x3));
+                       data = rand();
+               }
+               writel(data, addr);
+               if (test_loop_end(&loop, 0, 1000))
+                       break;
+       }
+       sprintf(string, "0x%x: %x", (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, nb_error = 0;
+       u32 loop = 0, nb_loop;
+
+       if (get_nb_loop(string, argc, argv, 0, &nb_loop, 1))
+               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++) {
+                       printf("execute %d:%s\n", (int)i, test[i].name);
+                       result = test[i].fct(ctl, phy, string, 0, NULL);
+                       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]", "Execute all tests", 1 },
+       {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]",
+        "write & test patterns: Mostly Zero, Mostly One and F/n",
+        1
+       },
+       {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]",
+        "basic test : infinite read access", 1},
+       {test_write, "infinite write", "[addr]",
+        "basic test : infinite write access", 1},
+};
+
+const int test_nb = ARRAY_SIZE(test);
diff --git a/drivers/ram/stm32mp1/stm32mp1_tests.h b/drivers/ram/stm32mp1/stm32mp1_tests.h
new file mode 100644 (file)
index 0000000..55f5d6d
--- /dev/null
@@ -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/drivers/ram/stm32mp1/stm32mp1_tuning.c b/drivers/ram/stm32mp1/stm32mp1_tuning.c
new file mode 100644 (file)
index 0000000..4e1c1fa
--- /dev/null
@@ -0,0 +1,1380 @@
+// SPDX-License-Identifier: GPL-2.0+ OR BSD-3-Clause
+/*
+ * Copyright (C) 2019, STMicroelectronics - All Rights Reserved
+ */
+#include <common.h>
+#include <console.h>
+#include <clk.h>
+#include <ram.h>
+#include <reset.h>
+#include <asm/io.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 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;
+
+       pr_debug("%s: [%x]: %x => DQ unit index = %x\n",
+                __func__, 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_ddrphy *phy)
+{
+       /* 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;
+       /* Specifies the maximum SDRAM row address to be used during
+        * BIST before the address & increments to the next bank.
+        */
+       u32 bmrow = 0x7FFF; /* To check */
+       /* Specifies the maximum SDRAM column address to be used during
+        * BIST before the address & increments to the next row.
+        */
+       u32 bmcol = 0x3FF;  /* To check */
+       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);
+
+       /* To check this line : */
+       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;
+
+       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 */
+       mdelay(1);
+
+       /*Perform BIST Run*/
+       clrsetbits_le32(&phy->bistrr,
+                       0x00000007,
+                       0x00000001);
+       /* Write BISTRR.BINST = 3?b001; */
+
+       /* Wait for a number of CTL clocks before reading BIST register*/
+       /* Wait 300 ctl_clk cycles;  ... IS it really needed?? */
+       /* Perform BIST Instruction Stop*/
+       /* Write BISTRR.BINST = 3?b010;*/
+
+       /* poll on BISTGSR.BDONE. If 0, wait.  ++TODO Add timeout */
+       while (!(readl(&phy->bistgsr) & DDRPHYC_BISTGSR_BDDONE))
+               ;
+
+       /*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);
+               pr_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)
+                       pr_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(phy);
+       pr_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;
+               }
+               pr_debug("\n======================\n");
+               pr_debug("Start deskew byte %d .\n", datx8);
+               pr_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) {
+                       pr_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 */
+                       pr_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
+                        */
+                       pr_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;
+               }
+
+               pr_debug("there is a pass region for phase idx %d\n",
+                        phase_idx);
+               pr_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) {
+                       pr_debug("Fail region (PHASE) found phase idx %d\n",
+                                phase_idx);
+                       pr_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;
+                               pr_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;
+                               pr_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++;
+                                       pr_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;
+                                       pr_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;
+                       pr_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);
+               pr_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;
+                       }
+                       pr_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;
+                               pr_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;
+                               pr_debug("Fail not found on bit %d => deskew[%d][%d] = %d\n",
+                                        bit_i, datx8, bit_i,
+                                        deskew_delay[datx8][bit_i]);
+                       }
+               }
+               pr_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;
+                       pr_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(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;
+
+               pr_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;
+               }
+               pr_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;
+               }
+               pr_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;
+               }
+               pr_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;
+               }
+               pr_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]++;
+                       pr_debug("** found phase : %d -  %d & unit %d - %d\n",
+                                right_bound.phase, left_bound.phase,
+                                right_bound.unit, left_bound.unit);
+                       pr_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) {
+                       pr_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];
+                               }
+                       }
+                       pr_debug("*******calculating mid region: system latency: %d  phase: %d********\n",
+                                dqs_gate_values[byte][0],
+                                dqs_gate_values[byte][1]);
+                       pr_debug("*******the nominal values were system latency: 0  phase: 2*******\n");
+                       set_r0dgsl_delay(phy, byte, dqs_gate_values[byte][0]);
+                       set_r0dgps_delay(phy, byte, dqs_gate_values[byte][1]);
+               }
+       } else {
+               /* if intermitant, restore defaut values */
+               pr_debug("dqs gating:no regular fail/pass/fail found. defaults values restored.\n");
+               set_r0dgsl_delay(phy, byte, 0);
+               set_r0dgps_delay(phy, byte, 2);
+       }
+
+       /* 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(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);
+       enum test_result res;
+
+       stm32mp1_refresh_disable(ctl);
+       res = read_dqs_gating(ctl, phy, string);
+       stm32mp1_refresh_restore(ctl, rfshctl3, pwrctl);
+
+       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);
+       enum test_result res;
+
+       stm32mp1_refresh_disable(ctl);
+       res = bit_deskew(ctl, phy, string);
+       stm32mp1_refresh_restore(ctl, rfshctl3, pwrctl);
+
+       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);
+       enum test_result res;
+
+       stm32mp1_refresh_disable(ctl);
+       res = eye_training(ctl, phy, string);
+       stm32mp1_refresh_restore(ctl, rfshctl3, pwrctl);
+
+       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);
index e31c87b9ac5ad2eae5a24397ef0f07a08b6e7d7d..cca8b707acc42c8d3d10486a4a359940cdbba290 100644 (file)
@@ -269,7 +269,6 @@ static inline void _debug_uart_init(void)
        _stm32_serial_setbrg(base, uart_info,
                             CONFIG_DEBUG_UART_CLOCK,
                             CONFIG_BAUDRATE);
-       printf("DEBUG done\n");
 }
 
 static inline void _debug_uart_putc(int c)
@@ -278,7 +277,7 @@ static inline void _debug_uart_putc(int c)
        struct stm32_uart_info *uart_info = _debug_uart_info();
 
        while (_stm32_serial_putc(base, uart_info, c) == -EAGAIN)
-               WATCHDOG_RESET();
+               ;
 }
 
 DEBUG_UART_FUNCS
index 70858d3b40755196495cbcfae38c50dbe3ca0980..1e10c7a4c46b1ea0370d5d8cb5d33d105e91eb15 100644 (file)
@@ -470,7 +470,7 @@ config ENV_EXT4_FILE
          It's a string of the EXT4 file name. This file use to store the
          environment (explicit path to the file)
 
-if ARCH_ROCKCHIP || ARCH_SUNXI || ARCH_ZYNQ || ARCH_ZYNQMP || ARCH_VERSAL || ARC
+if ARCH_ROCKCHIP || ARCH_SUNXI || ARCH_ZYNQ || ARCH_ZYNQMP || ARCH_VERSAL || ARC || ARCH_STM32MP
 
 config ENV_OFFSET
        hex "Environment Offset"
index 324502ed82924576c21ebc09e79e6c5c168b3bd2..bd340fe9d52d7bdfb62d68c83ceca6954ccf27ae 100644 (file)
@@ -23,7 +23,10 @@ DECLARE_GLOBAL_DATA_PTR;
 #include <env_default.h>
 
 struct hsearch_data env_htab = {
+#if CONFIG_IS_ENABLED(ENV_SUPPORT)
+       /* defined in flags.c, only compile with ENV_SUPPORT */
        .change_ok = env_flags_validate,
+#endif
 };
 
 /*
@@ -225,7 +228,9 @@ void env_relocate(void)
 #if defined(CONFIG_NEEDS_MANUAL_RELOC)
        env_reloc();
        env_fix_drivers();
-       env_htab.change_ok += gd->reloc_off;
+
+       if (env_htab.change_ok)
+               env_htab.change_ok += gd->reloc_off;
 #endif
        if (gd->env_valid == ENV_INVALID) {
 #if defined(CONFIG_ENV_IS_NOWHERE) || defined(CONFIG_SPL_BUILD)
index fd6c97a0c619dbfea5e70fe1fd6047a947424b6f..e8be51a1559d94c3de2e1f4feb71aa5cf5e7382a 100644 (file)
 #define CONFIG_ARMV7_SECURE_MAX_SIZE           STM32_SYSRAM_SIZE
 #endif
 
-/*
- * malloc() pool size
- */
-#define CONFIG_SYS_MALLOC_LEN                  SZ_32M
-
 /*
  * Configuration of the external SRAM memory used by U-Boot
  */
  */
 #define CONFIG_SYS_LOAD_ADDR                   STM32_DDR_BASE
 
-/*
- * Env parameters
- */
-#define CONFIG_ENV_SIZE                                SZ_4K
-
 /* ATAGs */
 #define CONFIG_CMDLINE_TAG
 #define CONFIG_SETUP_MEMORY_TAGS
@@ -95,8 +85,6 @@
  * for nand boot, boot with on ubifs partition on nand
  * for nor boot, use the default order
  */
-#define CONFIG_PREBOOT
-
 #define STM32MP_BOOTCMD "bootcmd_stm32mp=" \
        "echo \"Boot over ${boot_device}${boot_instance}!\";" \
        "if test ${boot_device} = serial || test ${boot_device} = usb;" \
index 08b32ba87dd512a1f5377d24d679dd97731427cd..ff3ec5f3f2b914752114f91c7518db0865c1973c 100644 (file)
@@ -14,6 +14,8 @@
 #define HEADER_VERSION_V1      0x1
 /* default option : bit0 => no signature */
 #define HEADER_DEFAULT_OPTION  (cpu_to_le32(0x00000001))
+/* default binary type for U-Boot */
+#define HEADER_TYPE_UBOOT      (cpu_to_le32(0x00000000))
 
 struct stm32_header {
        uint32_t magic_number;
@@ -29,7 +31,8 @@ struct stm32_header {
        uint32_t option_flags;
        uint32_t ecdsa_algorithm;
        uint32_t ecdsa_public_key[64 / 4];
-       uint32_t padding[84 / 4];
+       uint32_t padding[83 / 4];
+       uint32_t binary_type;
 };
 
 static struct stm32_header stm32image_header;
@@ -43,6 +46,7 @@ static void stm32image_default_header(struct stm32_header *ptr)
        ptr->header_version[VER_MAJOR_IDX] = HEADER_VERSION_V1;
        ptr->option_flags = HEADER_DEFAULT_OPTION;
        ptr->ecdsa_algorithm = 1;
+       ptr->binary_type = HEADER_TYPE_UBOOT;
 }
 
 static uint32_t stm32image_checksum(void *start, uint32_t len)
@@ -112,6 +116,8 @@ static void stm32image_print_header(const void *ptr)
               le32_to_cpu(stm32hdr->image_checksum));
        printf("Option     : 0x%08x\n",
               le32_to_cpu(stm32hdr->option_flags));
+       printf("BinaryType : 0x%08x\n",
+              le32_to_cpu(stm32hdr->binary_type));
 }
 
 static void stm32image_set_header(void *ptr, struct stat *sbuf, int ifd,