blob: c1f523eb3f4a1491f0bf40c822e4d3f8b117008a [file] [log] [blame]
Enrico Letod46260e2024-11-23 17:53:03 +01001// SPDX-License-Identifier: GPL-2.0+
2/*
3 * Copyright Siemens AG 2023
4 *
5 * DDR signal integrity test
6 * Check signals on DDR lines
7 * - signals must be as fast as possible and generate long burst
8 * - signals must be unidirectional (to DDR or from DDR only)
9 *
10 * Set pattern: define 2^n 32-bit patterns (up to 4)
11 * Addresses: must be multiple of 16 to avoid checks in loops
12 * Test functions
13 * - write: write pattern to memory area for iteration times
14 * - read: write pattern once to memory area, read for iteration times
15 */
16
17#include <command.h>
18#include <exports.h>
19#include <time.h>
20#if CONFIG_IS_ENABLED(AM33XX)
21#include <asm/arch-am33xx/hardware_am33xx.h>
22#include <asm/arch-am33xx/cpu.h>
23#include <asm/io.h>
24#endif
25
26/* enable some print for debugging */
27#ifdef PR_DEBUG
28 #define PDEBUG(fmt, args...) printf(fmt, ## args)
29#else
30 #define PDEBUG(fmt, args...)
31#endif
32
33/* define 4 32-bit patterns */
34#define MAX_PTN_SIZE (128)
35#define PTN_ARRAY_SIZE (MAX_PTN_SIZE / (8 * sizeof(u32)))
36
37/* define test direction */
38#define DIR_READ 0
39#define DIR_WRITE 1
40
41static union {
42 u64 l[2];
43 u32 s[4];
44 } test_pattern;
45static int num_ptn32;
46
47#if CONFIG_IS_ENABLED(AM33XX)
48static inline void wdt_disable(void)
49{
50 struct wd_timer *wdtimer = (struct wd_timer *)WDT_BASE;
51
52 writel(0xAAAA, &wdtimer->wdtwspr);
53 while (readl(&wdtimer->wdtwwps) != 0x0)
54 ;
55 writel(0x5555, &wdtimer->wdtwspr);
56 while (readl(&wdtimer->wdtwwps) != 0x0)
57 ;
58}
59
60static inline void wdt_enable(void)
61{
62 struct wd_timer *wdtimer = (struct wd_timer *)WDT_BASE;
63
64 writel(0xBBBB, &wdtimer->wdtwspr);
65 while (readl(&wdtimer->wdtwwps) != 0x0)
66 ;
67 writel(0x4444, &wdtimer->wdtwspr);
68 while (readl(&wdtimer->wdtwwps) != 0x0)
69 ;
70}
71#else /* ! */
72static inline void wdt_disable(void) {}
73
74static inline void wdt_enable(void) {}
75#endif /* CONFIG_IS_ENABLED(AM33XX) */
76
77static int do_ddr_set_ptn(struct cmd_tbl *cmdtp, int flag, int argc,
78 char *const argv[])
79{
80 int i, n;
81
82 if (argc < 1)
83 return CMD_RET_USAGE;
84
85 /* number of patterns: 2 exponent */
86 n = argc - 1;
87 if (n > PTN_ARRAY_SIZE || (n & (n - 1)))
88 return CMD_RET_USAGE;
89 num_ptn32 = n;
90
91 /* get patterns */
92 for (i = 0; i < n; i++)
93 test_pattern.s[i] = simple_strtoul(argv[i + 1], NULL, 0);
94
95 printf("Test pattern set\n");
96
97 return CMD_RET_SUCCESS;
98}
99
100static int do_ddr_show_ptn(struct cmd_tbl *cmdtp, int flag, int argc,
101 char *const argv[])
102{
103 if (!num_ptn32) {
104 printf("No pattern available\n");
105 } else {
106 u32 *buf = test_pattern.s;
107 int len = num_ptn32;
108 int i;
109
110 printf("Pattern: ");
111 for (i = 0 ; i < len; i++)
112 printf("0x%08X ", *buf++);
113
114 printf("\n");
115 }
116
117 return CMD_RET_SUCCESS;
118}
119
120static void ddr_read32(u64 start_addr, u64 n_word, unsigned long iter)
121{
122 while (iter--) {
123 register volatile u32 *addr = (u32 *)start_addr;
124 register u64 count = n_word;
125
126 while (count) {
127 (void)*addr++;
128 PDEBUG("Read 0x%08X from 0x%p\n", val, addr - 1);
129 count--;
130 }
131 }
132}
133
134static void ddr_read64(u64 start_addr, u64 n_word, unsigned long iter)
135{
136 while (iter--) {
137 register volatile u64 *addr = (u64 *)start_addr;
138 register u64 count = n_word;
139
140 if (num_ptn32 == 4)
141 count *= 2;
142
143 /*
144 * 64 & 128 bit pattern. Increase the nummber of read
145 * commands in the loop to generate longer burst signal
146 */
147 while (count) {
148 (void)*addr++;
149 PDEBUG("Read 0x%016llX from 0x%p\n", val, addr - 1);
150 (void)*addr++;
151 PDEBUG("Read 0x%016llX from 0x%p\n", val, addr - 1);
152 (void)*addr++;
153 PDEBUG("Read 0x%016llX from 0x%p\n", val, addr - 1);
154 (void)*addr++;
155 PDEBUG("Read 0x%016llX from 0x%p\n", val, addr - 1);
156 (void)*addr++;
157 PDEBUG("Read 0x%016llX from 0x%p\n", val, addr - 1);
158 (void)*addr++;
159 PDEBUG("Read 0x%016llX from 0x%p\n", val, addr - 1);
160 (void)*addr++;
161 PDEBUG("Read 0x%016llX from 0x%p\n", val, addr - 1);
162 (void)*addr++;
163 PDEBUG("Read 0x%016llX from 0x%p\n", val, addr - 1);
164 /*
165 * underflow cannot happen since n_word = end -
166 * start, end & start addresses are checked to be
167 * multiple of 16
168 */
169 count -= 8;
170 }
171 }
172}
173
174static void ddr_write32(u64 start_addr, u64 n_word, unsigned long iter)
175{
176 while (iter--) {
177 register u32 *addr = (u32 *)start_addr;
178 register u32 ptn = *test_pattern.s;
179 register u64 count = n_word;
180
181 while (count) {
182 PDEBUG("Write 0x%08X to 0x%p\n", ptn, addr);
183 *addr++ = ptn;
184 count--;
185 }
186 }
187}
188
189static void ddr_write64(u64 start_addr, u64 n_word, unsigned long iter)
190{
191 while (iter--) {
192 register u64 *addr = (u64 *)start_addr;
193 register u64 ptnA = test_pattern.l[0];
194 register u64 ptnB = test_pattern.l[1];
195 register u64 count = n_word;
196
197 if (num_ptn32 == 2)
198 ptnB = ptnA;
199 else
200 count *= 2;
201
202 /*
203 * 64 & 128 bit pattern. Increase the nummber of write
204 * commands in the loop to generate longer burst signal
205 */
206 while (count) {
207 PDEBUG("Write 0x%016llX to 0x%p\n", ptnA, addr);
208 *addr++ = ptnA;
209 PDEBUG("Write 0x%016llX to 0x%p\n", ptnB, addr);
210 *addr++ = ptnB;
211 PDEBUG("Write 0x%016llX to 0x%p\n", ptnA, addr);
212 *addr++ = ptnA;
213 PDEBUG("Write 0x%016llX to 0x%p\n", ptnB, addr);
214 *addr++ = ptnB;
215 PDEBUG("Write 0x%016llX to 0x%p\n", ptnA, addr);
216 *addr++ = ptnA;
217 PDEBUG("Write 0x%016llX to 0x%p\n", ptnB, addr);
218 *addr++ = ptnB;
219 PDEBUG("Write 0x%016llX to 0x%p\n", ptnA, addr);
220 *addr++ = ptnA;
221 PDEBUG("Write 0x%016llX to 0x%p\n", ptnB, addr);
222 *addr++ = ptnB;
223 /*
224 * underflow cannot happen since n_word = end -
225 * start, end & start addresses are checked to be
226 * multiple of 16
227 */
228 count -= 8;
229 }
230 }
231}
232
233static int do_ddr_si_test(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[])
234{
235 u64 start_addr, end_addr, n_word;
236 u64 ts_start, ts_end;
237 unsigned long iteration, wr_iter;
238 int direction, i;
239
240 if (argc < 3 || argc > 4)
241 return CMD_RET_USAGE;
242
243 /* get arguments */
244 direction = strcmp(argv[0], "read") ? DIR_WRITE : DIR_READ;
245 start_addr = simple_strtoul(argv[1], NULL, 0);
246 end_addr = simple_strtoul(argv[2], NULL, 0);
247 iteration = simple_strtoul(argv[3], NULL, 10);
248
249 n_word = (end_addr - start_addr) / (num_ptn32 * 4);
250 printf("\nDDR signal integrity %s test: start\n", argv[0]);
251 /* checks */
252 if (start_addr & 0xF) {
253 printf("ERROR: start_address should be 16 bytes aligned\n\n");
254 return CMD_RET_USAGE;
255 }
256
257 if (end_addr & 0xF) {
258 printf("ERROR: end_address should be 16 bytes aligned\n\n");
259 return CMD_RET_USAGE;
260 }
261
262 if (start_addr >= end_addr) {
263 printf("ERROR: end_address is not bigger than start_address\n\n");
264 return CMD_RET_USAGE;
265 }
266
267 if (!iteration) {
268 printf("ERROR: no iteration specified\n\n");
269 return CMD_RET_USAGE;
270 }
271
272 if (!num_ptn32) {
273 printf("ERROR: no test pattern specified\n\n");
274 return CMD_RET_USAGE;
275 }
276
277 /* print parameters */
278 printf("start_address = 0x%016llX\n", start_addr);
279 printf("end_address = 0x%016llX\n", end_addr);
280 printf("iterations = %lu\n", iteration);
281
282 /* print pattern */
283 printf("test pattern 0x");
284 for (i = 0; i < num_ptn32; i++)
285 printf("%08X", test_pattern.s[i]);
286
287 printf("\n");
288
289 wdt_disable();
290
291 /* writing */
292 printf("Writing..\n");
293 ts_start = get_timer_us(0);
294
295 if (direction == DIR_READ)
296 wr_iter = 1;
297 else
298 wr_iter = iteration;
299
300 if (num_ptn32 == 1)
301 ddr_write32(start_addr, n_word, wr_iter);
302 else
303 ddr_write64(start_addr, n_word, wr_iter);
304
305 ts_end = get_timer_us(0);
306
307 /* reading */
308 if (direction == DIR_READ) {
309 printf("Reading..\n");
310 /* we need read time, just overwrite */
311 ts_start = get_timer_us(0);
312
313 if (num_ptn32 == 1)
314 ddr_read32(start_addr, n_word, iteration);
315 else
316 ddr_read64(start_addr, n_word, iteration);
317
318 ts_end = get_timer_us(0);
319 }
320
321 wdt_enable();
322
323 /* print stats */
324 printf("DONE.");
325 printf(" Bytes=%llu ", n_word * num_ptn32 * 4 * iteration);
326 printf(" Time=%llu us ", ts_end - ts_start);
327 printf("\nDDR signal integrity %s test: end\n", argv[0]);
328
329 return CMD_RET_SUCCESS;
330}
331
332static char ddr_si_help_text[] =
333 "- DDR signal integrity test\n\n"
334 "ddr_si setptn <pattern> [<pattern>] : set [1,2,4] 32-bit patterns\n"
335 "ddr_si showptn : show patterns\n"
336 "ddr_si read <start> <end> <iterations> : run test for reading\n"
337 "ddr_si write <start> <end> <iterations> : run test for writing\n"
338 "\nWith\n"
339 "\t<pattern>: 32-bit pattern in hex format\n"
340 "\t<start>: test start address in hex format\n"
341 "\t<end>: test end address in hex format\n"
342 "\t<iterations>: number of iterations\n";
343
344U_BOOT_CMD_WITH_SUBCMDS(ddr_si, "DDR si test", ddr_si_help_text,
345 U_BOOT_SUBCMD_MKENT(setptn, 5, 0, do_ddr_set_ptn),
346 U_BOOT_SUBCMD_MKENT(showptn, 1, 0, do_ddr_show_ptn),
347 U_BOOT_SUBCMD_MKENT(read, 4, 0, do_ddr_si_test),
348 U_BOOT_SUBCMD_MKENT(write, 4, 0, do_ddr_si_test));