u-boot-brain/drivers/ram/stm32mp1/stm32mp1_tests.c
Patrick Delaunay 4b0496fe79 stm32mp1: ram: fix address issue in 2 tests
If user choose to test memory size is 1GByte (0x40000000),
memory address would overflow in test "Random" and
test "FrequencySelectivePattern".
Thus the system would hangs up when running DDR test.

Signed-off-by: Patrick Delaunay <patrick.delaunay@st.com>
Signed-off-by: Bossen WU <bossen.wu@st.com>
2019-08-27 11:19:23 +02:00

1430 lines
36 KiB
C

// 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, 8 * 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;
bufsize /= 2;
printf("running %d loops copy from 0x%x to 0x%x (buffer size=0x%x)\n",
nb_loop, addr, addr + bufsize, bufsize);
while (!error) {
seed = rand();
for (offset = 0; offset < bufsize; offset += 4)
writel(rand(), addr + offset);
memcpy((void *)addr + bufsize, (void *)addr, bufsize);
srand(seed);
for (offset = 0; offset < 2 * bufsize; offset += 4) {
if (offset == bufsize)
srand(seed);
value = rand();
error = check_addr(addr + offset, value);
if (error)
break;
if (progress(offset))
return TEST_FAILED;
}
if (test_loop_end(&loop, nb_loop, 100))
break;
}
putc('\n');
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 = (u32)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, #128");
__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);