diff options
-rw-r--r-- | board/siemens/capricorn/Kconfig | 2 | ||||
-rw-r--r-- | board/siemens/capricorn/Makefile | 1 | ||||
-rw-r--r-- | board/siemens/common/Kconfig | 4 | ||||
-rw-r--r-- | board/siemens/common/ddr_si_test.c | 348 |
4 files changed, 355 insertions, 0 deletions
diff --git a/board/siemens/capricorn/Kconfig b/board/siemens/capricorn/Kconfig index 03a433df2aa..fe230971e97 100644 --- a/board/siemens/capricorn/Kconfig +++ b/board/siemens/capricorn/Kconfig @@ -21,3 +21,5 @@ config SPL_CMT depends on SPL help Enable SIemens SPL RAM test. + +source "board/siemens/common/Kconfig" diff --git a/board/siemens/capricorn/Makefile b/board/siemens/capricorn/Makefile index b8350d96d04..a03d54ef3b3 100644 --- a/board/siemens/capricorn/Makefile +++ b/board/siemens/capricorn/Makefile @@ -11,4 +11,5 @@ obj-y += spl.o obj-$(CONFIG_SPL_CMT) += spl_memory_test.o else obj-y += ../common/factoryset.o +obj-$(CONFIG_DDR_SI_TEST) += ../common/ddr_si_test.o endif diff --git a/board/siemens/common/Kconfig b/board/siemens/common/Kconfig index 131439fcfea..4ae12b1c973 100644 --- a/board/siemens/common/Kconfig +++ b/board/siemens/common/Kconfig @@ -1,2 +1,6 @@ config FACTORYSET bool + +config DDR_SI_TEST + bool "DDR signal integrity test implementations" + default y diff --git a/board/siemens/common/ddr_si_test.c b/board/siemens/common/ddr_si_test.c new file mode 100644 index 00000000000..c1f523eb3f4 --- /dev/null +++ b/board/siemens/common/ddr_si_test.c @@ -0,0 +1,348 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright Siemens AG 2023 + * + * DDR signal integrity test + * Check signals on DDR lines + * - signals must be as fast as possible and generate long burst + * - signals must be unidirectional (to DDR or from DDR only) + * + * Set pattern: define 2^n 32-bit patterns (up to 4) + * Addresses: must be multiple of 16 to avoid checks in loops + * Test functions + * - write: write pattern to memory area for iteration times + * - read: write pattern once to memory area, read for iteration times + */ + +#include <command.h> +#include <exports.h> +#include <time.h> +#if CONFIG_IS_ENABLED(AM33XX) +#include <asm/arch-am33xx/hardware_am33xx.h> +#include <asm/arch-am33xx/cpu.h> +#include <asm/io.h> +#endif + +/* enable some print for debugging */ +#ifdef PR_DEBUG + #define PDEBUG(fmt, args...) printf(fmt, ## args) +#else + #define PDEBUG(fmt, args...) +#endif + +/* define 4 32-bit patterns */ +#define MAX_PTN_SIZE (128) +#define PTN_ARRAY_SIZE (MAX_PTN_SIZE / (8 * sizeof(u32))) + +/* define test direction */ +#define DIR_READ 0 +#define DIR_WRITE 1 + +static union { + u64 l[2]; + u32 s[4]; + } test_pattern; +static int num_ptn32; + +#if CONFIG_IS_ENABLED(AM33XX) +static inline void wdt_disable(void) +{ + struct wd_timer *wdtimer = (struct wd_timer *)WDT_BASE; + + writel(0xAAAA, &wdtimer->wdtwspr); + while (readl(&wdtimer->wdtwwps) != 0x0) + ; + writel(0x5555, &wdtimer->wdtwspr); + while (readl(&wdtimer->wdtwwps) != 0x0) + ; +} + +static inline void wdt_enable(void) +{ + struct wd_timer *wdtimer = (struct wd_timer *)WDT_BASE; + + writel(0xBBBB, &wdtimer->wdtwspr); + while (readl(&wdtimer->wdtwwps) != 0x0) + ; + writel(0x4444, &wdtimer->wdtwspr); + while (readl(&wdtimer->wdtwwps) != 0x0) + ; +} +#else /* ! */ +static inline void wdt_disable(void) {} + +static inline void wdt_enable(void) {} +#endif /* CONFIG_IS_ENABLED(AM33XX) */ + +static int do_ddr_set_ptn(struct cmd_tbl *cmdtp, int flag, int argc, + char *const argv[]) +{ + int i, n; + + if (argc < 1) + return CMD_RET_USAGE; + + /* number of patterns: 2 exponent */ + n = argc - 1; + if (n > PTN_ARRAY_SIZE || (n & (n - 1))) + return CMD_RET_USAGE; + num_ptn32 = n; + + /* get patterns */ + for (i = 0; i < n; i++) + test_pattern.s[i] = simple_strtoul(argv[i + 1], NULL, 0); + + printf("Test pattern set\n"); + + return CMD_RET_SUCCESS; +} + +static int do_ddr_show_ptn(struct cmd_tbl *cmdtp, int flag, int argc, + char *const argv[]) +{ + if (!num_ptn32) { + printf("No pattern available\n"); + } else { + u32 *buf = test_pattern.s; + int len = num_ptn32; + int i; + + printf("Pattern: "); + for (i = 0 ; i < len; i++) + printf("0x%08X ", *buf++); + + printf("\n"); + } + + return CMD_RET_SUCCESS; +} + +static void ddr_read32(u64 start_addr, u64 n_word, unsigned long iter) +{ + while (iter--) { + register volatile u32 *addr = (u32 *)start_addr; + register u64 count = n_word; + + while (count) { + (void)*addr++; + PDEBUG("Read 0x%08X from 0x%p\n", val, addr - 1); + count--; + } + } +} + +static void ddr_read64(u64 start_addr, u64 n_word, unsigned long iter) +{ + while (iter--) { + register volatile u64 *addr = (u64 *)start_addr; + register u64 count = n_word; + + if (num_ptn32 == 4) + count *= 2; + + /* + * 64 & 128 bit pattern. Increase the nummber of read + * commands in the loop to generate longer burst signal + */ + while (count) { + (void)*addr++; + PDEBUG("Read 0x%016llX from 0x%p\n", val, addr - 1); + (void)*addr++; + PDEBUG("Read 0x%016llX from 0x%p\n", val, addr - 1); + (void)*addr++; + PDEBUG("Read 0x%016llX from 0x%p\n", val, addr - 1); + (void)*addr++; + PDEBUG("Read 0x%016llX from 0x%p\n", val, addr - 1); + (void)*addr++; + PDEBUG("Read 0x%016llX from 0x%p\n", val, addr - 1); + (void)*addr++; + PDEBUG("Read 0x%016llX from 0x%p\n", val, addr - 1); + (void)*addr++; + PDEBUG("Read 0x%016llX from 0x%p\n", val, addr - 1); + (void)*addr++; + PDEBUG("Read 0x%016llX from 0x%p\n", val, addr - 1); + /* + * underflow cannot happen since n_word = end - + * start, end & start addresses are checked to be + * multiple of 16 + */ + count -= 8; + } + } +} + +static void ddr_write32(u64 start_addr, u64 n_word, unsigned long iter) +{ + while (iter--) { + register u32 *addr = (u32 *)start_addr; + register u32 ptn = *test_pattern.s; + register u64 count = n_word; + + while (count) { + PDEBUG("Write 0x%08X to 0x%p\n", ptn, addr); + *addr++ = ptn; + count--; + } + } +} + +static void ddr_write64(u64 start_addr, u64 n_word, unsigned long iter) +{ + while (iter--) { + register u64 *addr = (u64 *)start_addr; + register u64 ptnA = test_pattern.l[0]; + register u64 ptnB = test_pattern.l[1]; + register u64 count = n_word; + + if (num_ptn32 == 2) + ptnB = ptnA; + else + count *= 2; + + /* + * 64 & 128 bit pattern. Increase the nummber of write + * commands in the loop to generate longer burst signal + */ + while (count) { + PDEBUG("Write 0x%016llX to 0x%p\n", ptnA, addr); + *addr++ = ptnA; + PDEBUG("Write 0x%016llX to 0x%p\n", ptnB, addr); + *addr++ = ptnB; + PDEBUG("Write 0x%016llX to 0x%p\n", ptnA, addr); + *addr++ = ptnA; + PDEBUG("Write 0x%016llX to 0x%p\n", ptnB, addr); + *addr++ = ptnB; + PDEBUG("Write 0x%016llX to 0x%p\n", ptnA, addr); + *addr++ = ptnA; + PDEBUG("Write 0x%016llX to 0x%p\n", ptnB, addr); + *addr++ = ptnB; + PDEBUG("Write 0x%016llX to 0x%p\n", ptnA, addr); + *addr++ = ptnA; + PDEBUG("Write 0x%016llX to 0x%p\n", ptnB, addr); + *addr++ = ptnB; + /* + * underflow cannot happen since n_word = end - + * start, end & start addresses are checked to be + * multiple of 16 + */ + count -= 8; + } + } +} + +static int do_ddr_si_test(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[]) +{ + u64 start_addr, end_addr, n_word; + u64 ts_start, ts_end; + unsigned long iteration, wr_iter; + int direction, i; + + if (argc < 3 || argc > 4) + return CMD_RET_USAGE; + + /* get arguments */ + direction = strcmp(argv[0], "read") ? DIR_WRITE : DIR_READ; + start_addr = simple_strtoul(argv[1], NULL, 0); + end_addr = simple_strtoul(argv[2], NULL, 0); + iteration = simple_strtoul(argv[3], NULL, 10); + + n_word = (end_addr - start_addr) / (num_ptn32 * 4); + printf("\nDDR signal integrity %s test: start\n", argv[0]); + /* checks */ + if (start_addr & 0xF) { + printf("ERROR: start_address should be 16 bytes aligned\n\n"); + return CMD_RET_USAGE; + } + + if (end_addr & 0xF) { + printf("ERROR: end_address should be 16 bytes aligned\n\n"); + return CMD_RET_USAGE; + } + + if (start_addr >= end_addr) { + printf("ERROR: end_address is not bigger than start_address\n\n"); + return CMD_RET_USAGE; + } + + if (!iteration) { + printf("ERROR: no iteration specified\n\n"); + return CMD_RET_USAGE; + } + + if (!num_ptn32) { + printf("ERROR: no test pattern specified\n\n"); + return CMD_RET_USAGE; + } + + /* print parameters */ + printf("start_address = 0x%016llX\n", start_addr); + printf("end_address = 0x%016llX\n", end_addr); + printf("iterations = %lu\n", iteration); + + /* print pattern */ + printf("test pattern 0x"); + for (i = 0; i < num_ptn32; i++) + printf("%08X", test_pattern.s[i]); + + printf("\n"); + + wdt_disable(); + + /* writing */ + printf("Writing..\n"); + ts_start = get_timer_us(0); + + if (direction == DIR_READ) + wr_iter = 1; + else + wr_iter = iteration; + + if (num_ptn32 == 1) + ddr_write32(start_addr, n_word, wr_iter); + else + ddr_write64(start_addr, n_word, wr_iter); + + ts_end = get_timer_us(0); + + /* reading */ + if (direction == DIR_READ) { + printf("Reading..\n"); + /* we need read time, just overwrite */ + ts_start = get_timer_us(0); + + if (num_ptn32 == 1) + ddr_read32(start_addr, n_word, iteration); + else + ddr_read64(start_addr, n_word, iteration); + + ts_end = get_timer_us(0); + } + + wdt_enable(); + + /* print stats */ + printf("DONE."); + printf(" Bytes=%llu ", n_word * num_ptn32 * 4 * iteration); + printf(" Time=%llu us ", ts_end - ts_start); + printf("\nDDR signal integrity %s test: end\n", argv[0]); + + return CMD_RET_SUCCESS; +} + +static char ddr_si_help_text[] = + "- DDR signal integrity test\n\n" + "ddr_si setptn <pattern> [<pattern>] : set [1,2,4] 32-bit patterns\n" + "ddr_si showptn : show patterns\n" + "ddr_si read <start> <end> <iterations> : run test for reading\n" + "ddr_si write <start> <end> <iterations> : run test for writing\n" + "\nWith\n" + "\t<pattern>: 32-bit pattern in hex format\n" + "\t<start>: test start address in hex format\n" + "\t<end>: test end address in hex format\n" + "\t<iterations>: number of iterations\n"; + +U_BOOT_CMD_WITH_SUBCMDS(ddr_si, "DDR si test", ddr_si_help_text, + U_BOOT_SUBCMD_MKENT(setptn, 5, 0, do_ddr_set_ptn), + U_BOOT_SUBCMD_MKENT(showptn, 1, 0, do_ddr_show_ptn), + U_BOOT_SUBCMD_MKENT(read, 4, 0, do_ddr_si_test), + U_BOOT_SUBCMD_MKENT(write, 4, 0, do_ddr_si_test)); |