blob: 1a6651de7ea41a4ba842c3a2778fd40d1b86ee41 [file] [log] [blame]
#include "command.h"
#include "printf.h"
#include "mvUart.h"
#include "lib_utils.h"
#include "mv_os.h"
#include "soc_spec.h"
#include "ddr3_hws_hw_training_def.h"
#include "stdint.h"
#define DISP_LINE_LEN 16
#define MAX_LINE_LENGTH_BYTES (64)
#define DEFAULT_LINE_LENGTH_BYTES (16)
#define REG_PHY_DATA 0
#define CONFIG_SYS_MEMTEST_START 0x00400000
#define CONFIG_SYS_MEMTEST_END 0x007fffff
static unsigned int dp_last_addr, dp_last_size;
static unsigned int dp_last_length = 0x40;
static unsigned int mm_last_addr, mm_last_size;
/*******************************************************************************
* printHex
*
* DESCRIPTION:
* Prints unsigned value in hex with specified width
*
* INPUT:
* x - value to print
* width - data value width. May be 2, 4, or 8.
*
*******************************************************************************/
static void printHex(MV_U32 x, unsigned int width)
{
switch (width) {
case 8:
mvPrintf(" %08x", x);
break;
case 4:
mvPrintf(" %04x", x);
break;
default:
mvPrintf(" %02x", x);
break;
}
}
/*******************************************************************************
* print_buffer
*
* DESCRIPTION:
* Print data buffer in hex and ascii form to the terminal.
* data reads are buffered so that each memory address is only read once.
* Useful when displaying the contents of volatile registers.
*
* INPUT:
* addr - Starting address to display at start of line
* data - pointer to data buffer
* width - data value width. May be 1, 2, or 4.
* count - number of values to display
* linelen - Number of values to print per line
* specify 0 for default length
*
*******************************************************************************/
void print_buffer(unsigned long addr, void *data, unsigned int width, unsigned int count, unsigned int linelen)
{
/* linebuf as a union causes proper alignment */
union linebuf {
MV_U32 ui[MAX_LINE_LENGTH_BYTES/sizeof(MV_U32) + 1];
MV_U16 us[MAX_LINE_LENGTH_BYTES/sizeof(MV_U16) + 1];
MV_U8 uc[MAX_LINE_LENGTH_BYTES/sizeof(MV_U8) + 1];
} lb;
int i;
if (linelen*width > MAX_LINE_LENGTH_BYTES)
linelen = MAX_LINE_LENGTH_BYTES / width;
if (linelen < 1)
linelen = DEFAULT_LINE_LENGTH_BYTES / width;
while (count) {
mvPrintf("%08lx:", addr);
/* check for overflow condition */
if (count < linelen)
linelen = count;
/* Copy from memory into linebuf and print hex values */
for (i = 0; i < linelen; i++) {
MV_U32 x;
if (width == 4)
x = lb.ui[i] = *(volatile MV_U32*)data;
else if (width == 2)
x = lb.us[i] = *(volatile MV_U16*)data;
else
x = lb.uc[i] = *(volatile MV_U8*)data;
printHex(x, width*2);
data += width;
}
/* Print data in ASCII characters */
for (i = 0; i < linelen * width; i++) {
if (!isprint(lb.uc[i]) || lb.uc[i] >= 0x80)
lb.uc[i] = '.';
}
lb.uc[i] = '\0';
mvPrintf(" %s\n", lb.uc);
/* update references */
addr += linelen * width;
count -= linelen;
}
}
/*******************************************************************************
* align_address
*
* DESCRIPTION:
* Unaligned memory access Workaround:
* if size = long/word, & address not aligned to long/word (respectively)
* align address to meet requested size
*
* INPUT:
* addr - address in memory to align (1/2/4)
* size - size in bytes
*
* RETURN:
* the aligned address
*
*******************************************************************************/
static unsigned long align_address(unsigned long addr, int size)
{
if ((size > 1) && (addr % size > 0)) {
mvPrintf("Error: Requested unaligned memory address (0x%x)\n", (unsigned int)addr);
addr &= ~(size - 1);
mvPrintf("Using aligned address (0x%x)\n", (unsigned int)addr);
}
return addr;
}
/*******************************************************************************
* do_mem_md
*
* DESCRIPTION:
* The command allows the user to display memory values
*
*******************************************************************************/
int do_mem_md(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
{
unsigned long addr, length;
int size;
int rc = 0;
/* We use the last specified parameters, unless new ones are
* entered.
*/
addr = dp_last_addr;
size = dp_last_size;
length = dp_last_length;
if (argc < 2)
return CMD_RET_USAGE;
if ((flag & CMD_FLAG_REPEAT) == 0) {
/* New command specified. Check for a size specification.
* Defaults to long if no or incorrect specification.
*/
size = cmd_get_data_size(argv[0], 4);
if (size < 0)
return 1;
/* Address is specified since argc > 1
*/
addr = simple_strtoul(argv[1], NULL, 16);
/* If another parameter, it is the length to display.
* Length is the number of objects, not number of bytes.
*/
if (argc > 2)
length = simple_strtoul(argv[2], NULL, 16);
}
addr = align_address(addr, size);
/* Print the lines. */
print_buffer(addr, (void *)addr, size, length, DISP_LINE_LEN/size);
addr += size*length;
dp_last_addr = addr;
dp_last_length = length;
dp_last_size = size;
return rc;
}
/*******************************************************************************
* do_mem_mw
*
* DESCRIPTION:
* The command allows the user to write to memory
*
*******************************************************************************/
int do_mem_mw(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
{
unsigned long addr, writeval, count;
int size;
if ((argc < 3) || (argc > 4))
return CMD_RET_USAGE;
/* Check for size specification.
*/
size = cmd_get_data_size(argv[0], 4);
if (size < 1)
return 1;
/* Address is specified since argc > 1
*/
addr = simple_strtoul(argv[1], NULL, 16);
addr = align_address(addr, size);
/* Get the value to write.
*/
writeval = simple_strtoul(argv[2], NULL, 16);
/* Count ? */
if (argc == 4)
count = simple_strtoul(argv[3], NULL, 16);
else
count = 1;
while (count-- > 0) {
if (size == 4)
*((unsigned long *)addr) = (unsigned long)writeval;
else if (size == 2)
*((unsigned short *)addr) = (unsigned short)writeval;
else
*((unsigned char *)addr) = (unsigned char)writeval;
addr += size;
}
return 0;
}
static int mod_mem(cmd_tbl_t *cmdtp, int incrflag, int flag, int argc, char * const argv[])
{
unsigned long addr, i;
int nbytes, size;
if (argc != 2)
return CMD_RET_USAGE;
/* We use the last specified parameters, unless new ones are
* entered.
*/
addr = mm_last_addr;
size = mm_last_size;
if ((flag & CMD_FLAG_REPEAT) == 0) {
/* New command specified. Check for a size specification.
* Defaults to long if no or incorrect specification.
*/
size = cmd_get_data_size(argv[0], 4);
if (size < 0)
return 1;
/* Address is specified since argc > 1
*/
addr = simple_strtoul(argv[1], NULL, 16);
}
addr = align_address(addr, size);
/* Print the address, followed by value. Then accept input for
* the next value. A non-converted value exits.
*/
do {
mvPrintf("%08lx:", addr);
if (size == 4)
mvPrintf(" %08x", *((unsigned int *)addr));
else if (size == 2)
mvPrintf(" %04x", *((unsigned short *)addr));
else
mvPrintf(" %02x", *((unsigned char *)addr));
nbytes = readline(" ? ");
if (nbytes == 0 || (nbytes == 1 && console_buffer[0] == '-')) {
/* <CR> pressed as only input, don't modify current
* location and move to next. "-" pressed will go back.
*/
if (incrflag)
addr += nbytes ? -size : size;
nbytes = 1;
} else {
char *endp;
i = simple_strtoul(console_buffer, &endp, 16);
nbytes = endp - console_buffer;
if (nbytes) {
if (size == 4)
*((unsigned int *)addr) = i;
else if (size == 2)
*((unsigned short *)addr) = i;
else
*((unsigned char *)addr) = i;
if (incrflag)
addr += size;
}
}
} while (nbytes);
mm_last_addr = addr;
mm_last_size = size;
return 0;
}
/*******************************************************************************
* do_mem_mm
*
* DESCRIPTION:
* The command allows the user to modify bytes in memory
*
*******************************************************************************/
int do_mem_mm(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
{
return mod_mem(cmdtp, 1, flag, argc, argv);
}
/*******************************************************************************
* do_mem_cmp
*
* DESCRIPTION:
* The command allows the user to compare bytes in memory
*
*******************************************************************************/
int do_mem_cmp(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
{
unsigned long addr1, addr2, count, ngood;
int size;
int rcode = 0;
const char *type;
if (argc != 4)
return CMD_RET_USAGE;
/* Check for size specification.
*/
size = cmd_get_data_size(argv[0], 4);
if (size < 0)
return 1;
type = size == 4 ? "word" : size == 2 ? "halfword" : "byte";
addr1 = simple_strtoul(argv[1], NULL, 16);
addr2 = simple_strtoul(argv[2], NULL, 16);
addr1 = align_address(addr1, size);
addr2 = align_address(addr2, size);
count = simple_strtoul(argv[3], NULL, 16);
for (ngood = 0; ngood < count; ++ngood) {
unsigned long word1, word2;
if (size == 4) {
word1 = *(unsigned long *)addr1;
word2 = *(unsigned long *)addr2;
} else if (size == 2) {
word1 = *(unsigned short *)addr1;
word2 = *(unsigned short *)addr2;
} else {
word1 = *(unsigned char *)addr1;
word2 = *(unsigned char *)addr2;
}
if (word1 != word2) {
mvPrintf("%s at 0x%08lx (%#0*lx) != %s at 0x%08lx (%#0*lx)\n",
type, addr1, size, word1,
type, addr2, size, word2);
rcode = 1;
break;
}
addr1 += size;
addr2 += size;
}
mvPrintf("Total of %ld %s(s) were the same\n", ngood, type);
return rcode;
}
/*******************************************************************************
* do_mem_cp
*
* DESCRIPTION:
* The command allows the user to copy bytes from/to memory
*
*******************************************************************************/
int do_mem_cp(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
{
unsigned long addr, dest, count;
int size;
if (argc != 4)
return CMD_RET_USAGE;
/* Check for size specification.
*/
size = cmd_get_data_size(argv[0], 4);
if (size < 0)
return 1;
addr = simple_strtoul(argv[1], NULL, 16);
dest = simple_strtoul(argv[2], NULL, 16);
addr = align_address(addr, size);
dest = align_address(dest, size);
count = simple_strtoul(argv[3], NULL, 16);
if (count == 0) {
mvPrintf("Zero length ???\n");
return 1;
}
while (count-- > 0) {
if (size == 4)
*((unsigned long *)dest) = *((unsigned long *)addr);
else if (size == 2)
*((unsigned short *)dest) = *((unsigned short *)addr);
else
*((unsigned char *)dest) = *((unsigned char *)addr);
addr += size;
dest += size;
}
return 0;
}
/*******************************************************************************
* ir_cmd
*
* DESCRIPTION:
* The command allows the user to view the contents of the MV
* internal registers and modify them.
*
*******************************************************************************/
int ir_cmd(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
{
MV_U32 regNum = 0x0, regVal, regValTmp, res;
MV_8 regValBin[40];
MV_8 cmd[40];
int i;
int j = 0, flagm = 0;
if (argc == 2)
regNum = simple_strtoul(argv[1], NULL, 16);
else {
mvPrintf("Usage:\n%s\n", cmdtp->usage);
return 0;
}
regVal = MV_REG_READ(regNum + INTER_REGS_BASE);
regValTmp = regVal;
mvPrintf("Internal register 0x%x value : 0x%x\n ", regNum, regVal);
mvPrintf("\n 31 24 16 8 0");
mvPrintf("\n | | | | |\nOLD: ");
for (i = 31; i >= 0; i--) {
if (regValTmp > 0) {
res = regValTmp % 2;
regValTmp = (regValTmp - res) / 2;
if (res == 0)
regValBin[i] = '0';
else
regValBin[i] = '1';
} else
regValBin[i] = '0';
}
for (i = 0; i < 32; i++) {
mvPrintf("%c", regValBin[i]);
if ((((i+1) % 4) == 0) && (i > 1) && (i < 31))
mvPrintf("-");
}
readline("\nNEW: ");
strcpy(cmd, console_buffer);
if ((cmd[0] == '0') && (cmd[1] == 'x')) {
regVal = simple_strtoul(cmd, NULL, 16);
flagm = 1;
} else {
for (i = 0; i < 40; i++) {
if (cmd[i] == '\0')
break;
if (i == 4 || i == 9 || i == 14 || i == 19 || i == 24 || i == 29 || i == 34)
continue;
if (cmd[i] == '1') {
regVal = regVal | (0x80000000 >> j);
flagm = 1;
} else if (cmd[i] == '0') {
regVal = regVal & (~(0x80000000 >> j));
flagm = 1;
}
j++;
}
}
if (flagm == 1) {
MV_REG_WRITE(regNum + INTER_REGS_BASE, regVal);
mvPrintf("\nNew value = 0x%x\n\n", MV_REG_READ(regNum +
INTER_REGS_BASE));
}
return 1;
}
unsigned int trainingReadPhyReg(int uiRegAddr, int uiPup, int type)
{
unsigned int uiReg;
unsigned int addrLow = 0x3F & uiRegAddr;
unsigned int addrHi = ((0xC0 & uiRegAddr) >> 6);
uiReg = (1 << 31) + (uiPup << 22) + (type << 26) + (addrHi << 28) + (addrLow << 16);
MV_REG_WRITE(REG_PHY_REGISTRY_FILE_ACCESS_ADDR, uiReg & 0x7FFFFFFF);
MV_REG_WRITE(REG_PHY_REGISTRY_FILE_ACCESS_ADDR, uiReg);
do {
uiReg = ((MV_REG_READ(REG_PHY_REGISTRY_FILE_ACCESS_ADDR)) & (1 << 31));
} while (uiReg); /* Wait for '0' to mark the end of the transaction */
uiReg = MV_REG_READ(REG_PHY_REGISTRY_FILE_ACCESS_ADDR);
uiReg = uiReg & 0xFFFF;
return uiReg;
}
/*******************************************************************************
* training_cmd
*
* DESCRIPTION:
* The command prints the results of the DDR3 Training
*
*******************************************************************************/
int training_cmd(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
{
MV_U32 uiCsEna, uiCs, uiReg, uiPup, uiPhase, uiDelay;
MV_U32 uiRdRdyDly, uiRdSmplDly, uiDq, uiCentralTxRes, uiCentralRxRes;
MV_U32 uiPupNum;
uiCsEna = MV_REG_READ(REG_DDR3_RANK_CTRL_ADDR) & 0xF;
mvPrintf("DDR3 Training results:\n");
uiPupNum = 5;
for (uiCs = 0; uiCs < 4; uiCs++) {
if (uiCsEna & (1 << uiCs)) {
mvPrintf("CS: %d\n", uiCs);
for (uiPup = 0; uiPup < uiPupNum; uiPup++) {
uiReg = trainingReadPhyReg(0 + uiCs*4, uiPup, REG_PHY_DATA);
uiPhase = (uiReg >> 6) & 0x7;
uiDelay = uiReg & 0x1F;
mvPrintf("Write Leveling: PUP: %d, Phase: %d, Delay: %d\n", uiPup, uiPhase, uiDelay);
}
for (uiPup = 0; uiPup < uiPupNum; uiPup++) {
uiReg = trainingReadPhyReg(2 + uiCs*4, uiPup, REG_PHY_DATA);
uiPhase = (uiReg >> 6) & 0x7;
uiDelay = uiReg & 0x1F;
mvPrintf("Read Leveling: PUP: %d, Phase: %d, Delay: %d\n", uiPup, uiPhase, uiDelay);
}
uiRdRdyDly = ((MV_REG_READ(REG_READ_DATA_READY_DELAYS_ADDR) >> (8*uiCs)) & 0x1F);
uiRdSmplDly = ((MV_REG_READ(REG_READ_DATA_SAMPLE_DELAYS_ADDR) >> (8*uiCs)) & 0x1F);
mvPrintf("Read Sample Delay:\t%d\n", uiRdSmplDly);
mvPrintf("Read Ready Delay:\t%d\n", uiRdRdyDly);
/* PBS */
if (uiCs == 0) {
for (uiPup = 0; uiPup < uiPupNum; uiPup++) {
for (uiDq = 0; uiDq < 10; uiDq++) {
if (uiDq == 9)
uiDq++;
uiReg = trainingReadPhyReg(0x10+uiDq+uiCs*0x12, uiPup, REG_PHY_DATA);
uiDelay = uiReg & 0x1F;
if (uiDq == 0)
mvPrintf("PBS TX: PUP: %d: ", uiPup);
mvPrintf("%d ", uiDelay);
}
mvPrintf("\n");
}
for (uiPup = 0; uiPup < uiPupNum; uiPup++) {
for (uiDq = 0; uiDq < 9; uiDq++) {
uiReg = trainingReadPhyReg(0x50 + uiDq + uiCs*0x12,
uiPup, REG_PHY_DATA);
uiDelay = uiReg & 0x1F;
if (uiDq == 0)
mvPrintf("PBS RX: PUP: %d: ", uiPup);
mvPrintf("%d ", uiDelay);
}
mvPrintf("\n");
}
}
/*Read Centralization windows sizes for Scratch PHY registers*/
for (uiPup = 0; uiPup < uiPupNum; uiPup++) {
uiReg = trainingReadPhyReg(0xC0 + uiCs, uiPup, REG_PHY_DATA);
uiCentralRxRes = uiReg >> 5;
uiCentralTxRes = uiReg & 0x1F;
mvPrintf("Central window size for PUP %d is %d(RX) and %d(TX)\n",
uiPup, uiCentralRxRes, uiCentralTxRes);
}
}
}
return 1;
}
/*******************************************************************************
* do_mem_mtest
*
* DESCRIPTION:
* Perform a memory test. A more complete alternative test can be
* configured using CONFIG_SYS_ALT_MEMTEST.
*
*******************************************************************************/
int do_mem_mtest(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
{
volatile unsigned long *addr, *start, *end;
unsigned long val;
unsigned long readback;
unsigned long errs = 0;
int iterations = 1;
int iteration_limit;
unsigned long incr;
unsigned long pattern;
if (argc > 1)
start = (unsigned long *)simple_strtoul(argv[1], NULL, 16);
else
start = (unsigned long *)CONFIG_SYS_MEMTEST_START;
if (argc > 2)
end = (unsigned long *)simple_strtoul(argv[2], NULL, 16);
else
end = (unsigned long *)(CONFIG_SYS_MEMTEST_END);
start = (unsigned long *)align_address((unsigned long)start, 4);
end = (unsigned long *)align_address((unsigned long)end, 4);
if (argc > 3)
pattern = (unsigned long)simple_strtoul(argv[3], NULL, 16);
else
pattern = 0;
if (argc > 4)
iteration_limit = (unsigned long)simple_strtoul(argv[4], NULL, 16);
else
iteration_limit = 0;
incr = 1;
for (;;) {
if (iteration_limit && iterations > iteration_limit) {
mvPrintf("Tested %d iteration(s) with %lu errors.\n",
iterations-1, errs);
return errs != 0;
}
++iterations;
mvPrintf("\rPattern %08lX Writing...", pattern);
mvPrintf("%12s", "");
mvPrintf("\b\b\b\b\b\b\b\b\b\b");
for (addr = start, val = pattern; addr < end; addr++) {
*addr = val;
val += incr;
}
mvPrintf("Reading...");
for (addr = start, val = pattern; addr < end; addr++) {
readback = *addr;
if (readback != val) {
mvPrintf("\nMem error @");
mvPrintf(" 0x%08X: found %08lX, expected %08lX\n",
(unsigned int)(uintptr_t)addr, readback, val);
errs++;
}
val += incr;
}
/*
* Flip the pattern each time to make lots of zeros and
* then, the next time, lots of ones. We decrement
* the "negative" patterns and increment the "positive"
* patterns to preserve this feature.
*/
if (pattern & 0x80000000)
pattern = -pattern; /* complement & increment */
else
pattern = ~pattern;
incr = -incr;
}
return 0; /* not reached */
}