// 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 #include #include #if CONFIG_IS_ENABLED(AM33XX) #include #include #include #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 [] : set [1,2,4] 32-bit patterns\n" "ddr_si showptn : show patterns\n" "ddr_si read : run test for reading\n" "ddr_si write : run test for writing\n" "\nWith\n" "\t: 32-bit pattern in hex format\n" "\t: test start address in hex format\n" "\t: test end address in hex format\n" "\t: 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));