stm32mp1: ram: add tests in DDR interactive mode
authorPatrick Delaunay <patrick.delaunay@st.com>
Wed, 10 Apr 2019 12:09:28 +0000 (14:09 +0200)
committerPatrice Chotard <patrice.chotard@st.com>
Thu, 23 May 2019 09:38:11 +0000 (11:38 +0200)
Add command tests for DDR interactive mode, used during
board bring-up or with CubeMX DDR tools to verify the
DDR configuration.

Signed-off-by: Patrick Delaunay <patrick.delaunay@st.com>
drivers/ram/stm32mp1/Kconfig
drivers/ram/stm32mp1/Makefile
drivers/ram/stm32mp1/stm32mp1_interactive.c
drivers/ram/stm32mp1/stm32mp1_tests.c [new file with mode: 0644]
drivers/ram/stm32mp1/stm32mp1_tests.h [new file with mode: 0644]

index 511eaf2c4c32dbb75ad1b3ab6f0aeba12d0d16a2..4e682b4edff2f7d27fbf45d724829b14b91a2d51 100644 (file)
@@ -29,3 +29,11 @@ config STM32MP1_DDR_INTERACTIVE_FORCE
                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
index 7b1f095f90734d5a9dcf2b549b4839fa2c61c7ca..71ded6bed40f51ef1aa43adcde7ce2aa67e48175 100644 (file)
@@ -7,6 +7,7 @@ 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
 
 ifneq ($(DDR_INTERACTIVE),)
 CFLAGS_stm32mp1_interactive.o += -DCONFIG_STM32MP1_DDR_INTERACTIVE_FORCE=y
index 1a31948cb17da2473d4e9c030e7b24187e4c7099..62d61ac869b42ecac67c754363f9de688353a8ec 100644 (file)
@@ -11,6 +11,7 @@
 #include <ram.h>
 #include <reset.h>
 #include "stm32mp1_ddr.h"
+#include "stm32mp1_tests.h"
 
 DECLARE_GLOBAL_DATA_PTR;
 
@@ -51,6 +52,9 @@ enum ddr_command stm32mp1_get_command(char *cmd, int argc)
                [DDR_CMD_STEP] = "step",
                [DDR_CMD_NEXT] = "next",
                [DDR_CMD_GO] = "go",
+#ifdef CONFIG_STM32MP1_DDR_TESTS
+               [DDR_CMD_TEST] = "test",
+#endif
        };
        /* min and max number of argument */
        const char cmd_arg[DDR_CMD_UNKNOWN][2] = {
@@ -64,6 +68,9 @@ enum ddr_command stm32mp1_get_command(char *cmd, int argc)
                [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
        };
        int i;
 
@@ -105,6 +112,9 @@ static void stm32mp1_do_usage(void)
                "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
                "\nwith for [type|reg]:\n"
                "  all registers if absent\n"
                "  <type> = ctl, phy\n"
@@ -287,6 +297,63 @@ end:
        return step;
 }
 
+#if defined(CONFIG_STM32MP1_DDR_TESTS)
+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)
@@ -382,6 +449,14 @@ bool stm32mp1_ddr_interactive(void *priv,
                        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
+
                default:
                        break;
                }
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..8436780
--- /dev/null
@@ -0,0 +1,31 @@
+/* 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;
+
+#endif