blob: 733322d163e986ab27ad373806cae00f76b802a4 [file] [log] [blame]
/*
* (C) Copyright Mindspeed Technologies Inc.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation; either version 2 of
* the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston,
* MA 02111-1307 USA
*/
#include <init.h>
#include <common.h>
#include <asm/io.h>
#include <command.h>
#include <errno.h>
//#define C2K_DDR_TRAINING_DEBUG
#undef C2K_DDR_TRAINING_DEBUG
#if defined(C2K_DDR_TRAINING_DEBUG)
#define DPRINT_LVL0(args) printf args
#define DPRINT_LVL1(args) printf args
#define DPRINT_LVL2(args) printf args
#define DPRINT_LIST(ds,max,adj2_dsx) { \
for (read_offset=0; read_offset < max; read_offset++) \
printf("%d,",(adj2_dsx[read_offset]>>(ds*8))&0xFF);\
printf("\n");\
}
#else
#define DPRINT_LVL0(args) printf args
#define DPRINT_LVL1(args)
#define DPRINT_LVL2(args)
#define DPRINT_LIST(ds,max,adj2_dsx)
#endif
#define SZ_1K 0x400
#define SZ_128K (SZ_1K * 128)
#define ADDR_JUMP_SIZE SZ_128K
#define PHYS_SDRAM_SIZE (256*1024*1024)
#define DDR_BASEADDR 0x0
#define CTRL_LOCK_SHIFT 4
#define CTRL_LOCK_MASK 0x3ff
#define PHY_RD_VLD_MASK 0x7fffffff
/* DDR PHY Registers */
#define DDR_PHY_CTL_00_REG (0x905B0000 + 0x00)
#define DDR_PHY_CTL_01_REG (0x905B0000 + 0x04)
#define DDR_PHY_CTL_02_REG (0x905B0000 + 0x08)
#define DDR_PHY_CTL_03_REG (0x905B0000 + 0x0C)
#define DDR_PHY_CTL_04_REG (0x905B0000 + 0x10)
#define DDR_PHY_CTL_05_REG (0x905B0000 + 0x14)
#define DDR_PHY_CTL_06_REG (0x905B0000 + 0x18)
#define DDR_PHY_CTL_07_REG (0x905B0000 + 0x1C)
#define DDR_PHY_DLL_STAT_REG (0x905B0000 + 0x70)
#define DDR_PHY_ZQ_STAT_REG (0x905B0000 + 0x74)
#define CTRL_RESYNC_EN (1 << 20)
#define CTRL_RESYNC_PLS (1 << 21)
#define ADJ1_MIN_ACCEPTED_WINDOW 50
#define ADJ1_ACCEPTED_WINDOW 55
#define ADJ2_MIN_ACCEPTED_RANGE 5
#define ADJ2_ACCEPTED_RANGE 8
static u8 do_wr_rd_transaction(u32 ddr_addr_offset, u64*, u32*, u16 mode);
typedef struct adj2_ds_s
{
u8 win_start;
u8 win_end;
u8 win;
u8 gWin;
u8 gWin_start;
u8 gWin_end;
u8 inx;
}adj2_ds_t;
typedef struct adj2_val_s
{
adj2_ds_t ds0;
adj2_ds_t ds1;
adj2_ds_t ds2;
adj2_ds_t ds3;
}adj2_val_t;
/********************************************************************
* enable_resync:
*
*******************************************************************/
void enable_resync(void)
{
u32 ctrl_val;
//Enable PHY ctrl_resync
writel(0x80000000, DDR_PHY_CTL_07_REG);
ctrl_val = readl(DDR_PHY_CTL_07_REG) & PHY_RD_VLD_MASK;
ctrl_val |= CTRL_RESYNC_EN;
writel(ctrl_val, DDR_PHY_CTL_07_REG);
}
/********************************************************************
* disable_resync:
*
*******************************************************************/
void disable_resync(void)
{
u32 ctrl_val;
//Clear PHY ctrl_resync
writel(0x80000000, DDR_PHY_CTL_07_REG);
ctrl_val = readl(DDR_PHY_CTL_07_REG) & PHY_RD_VLD_MASK;
ctrl_val &= ~CTRL_RESYNC_EN;
writel(ctrl_val, DDR_PHY_CTL_07_REG);
}
/********************************************************************
* assert_resync:
*
*******************************************************************/
void assert_resync(void)
{
u32 ctrl_val;
//Assert ctrl_resync pulse
writel(0x80000000, DDR_PHY_CTL_07_REG);
ctrl_val = readl(DDR_PHY_CTL_07_REG) & PHY_RD_VLD_MASK;
ctrl_val |= CTRL_RESYNC_PLS;
writel(ctrl_val, DDR_PHY_CTL_07_REG);
//clear it
ctrl_val &= ~CTRL_RESYNC_PLS;
writel(ctrl_val, DDR_PHY_CTL_07_REG);
}
/********************************************************************
* write_dq_offset_req:
* Write to the Control Register 4 and 5 to set the delay offset to
* write DQ values for each byte
*******************************************************************/
void write_dq_offset_reg(u8 byte0, u8 byte1, u8 byte2, u8 byte3)
{
u32 ctrl4_val;
u32 ctrl5_val;
u32 ctrl_val;
//Write the first 3 byte in Control Register 4
writel(0x80000000, DDR_PHY_CTL_04_REG);
ctrl4_val = readl(DDR_PHY_CTL_04_REG) & PHY_RD_VLD_MASK;
ctrl_val = (byte2 << 24) | (byte1 << 16) |
(byte0 << 8) | (ctrl4_val & 0x7f);
writel(ctrl_val, DDR_PHY_CTL_04_REG);
//Write the 4th byte in Control Register 5
writel(0x80000000, DDR_PHY_CTL_05_REG);
ctrl5_val = readl(DDR_PHY_CTL_05_REG) & PHY_RD_VLD_MASK;
ctrl_val = (byte3) | (ctrl5_val & ~0x7f);
writel(ctrl_val, DDR_PHY_CTL_05_REG);
assert_resync();
}
/********************************************************************
* read_dqs_offset_req:
* Write to the Control Register 3 to set the delay offset to
* read DQS values for each byte
********************************************************************/
void read_dqs_offset_reg(u8 byte0, u8 byte1, u8 byte2, u8 byte3)
{
u32 ctrl_val;
ctrl_val = (byte3 << 24) | (byte2 << 16) |
(byte1 << 8) | (byte0);
writel(ctrl_val, DDR_PHY_CTL_03_REG);
assert_resync();
}
/**********************************************************************
* calOffset:
*
*********************************************************************/
u8 calOffset(int offset_ctrl, u32 ctrl_offset_max)
{
u8 offset;
if (offset_ctrl < ctrl_offset_max) {
//bit[6] should be 1 for negative delay
offset = (((ctrl_offset_max - offset_ctrl) & 0x3F) | (1 << 6));
}
else {
//bit[6] should be 0 for positive delay
offset = (((offset_ctrl - ctrl_offset_max) & 0x3F) | (0 << 6));
}
return offset;
}
/*********************************************************************
* adj2_dsx_calculate_window:
*
********************************************************************/
static void adj2_dsx_calculate_window(u8 max_win_size, u8 result,
u8 offsetw, u8 offsetr,
adj2_ds_t *adj2)
{
u8 win;
if (!(result & 0x0F)) /* success */
{
/* its first time success, start counting from here */
if (adj2->win_start == 0)
{
adj2->win_start = adj2->win_end = offsetw;
}
else {
adj2->win_end++;
/* close the window, if we reach
the end point of adj1 range */
if (offsetw == max_win_size)
{
win = (adj2->win_end - adj2->win_start) + 1;
if (adj2->win < win)
adj2->win = win;
DPRINT_LVL2(("[ds%d:%d..%d=%d]\n",result >> 4, adj2->win_start, adj2->win_end, win));
/* is this window bigger than
the previous one, then take it */
if (adj2->gWin < win)
{
adj2->gWin = win;
adj2->gWin_start = adj2->win_start;
adj2->gWin_end = adj2->win_end;
adj2->inx = offsetr;
}
}
}
}
else /* failure */
{
if (adj2->win_start == 0)
{
//window not yet started, so nothing to do
}
else
{
//this window ends here,calculate window size
win = (adj2->win_end - adj2->win_start) + 1;
DPRINT_LVL2(("[ds%d:%d..%d=%d]\n",result >> 4, adj2->win_start, adj2->win_end, win));
if (adj2->win < win)
{
adj2->win = win;
}
//is this window bigger than the previous one,
if (adj2->gWin < win)
{
adj2->gWin = win;
adj2->gWin_start = adj2->win_start;
adj2->gWin_end = adj2->win_end;
adj2->inx = offsetr;
}
/* reset window pointer to measure next window */
adj2->win_start = adj2->win_end = 0;
}
} /* failure */
return;
}
/********************************************************************
* get_adj2_adjusted_value:
* for basing on adj2 min value logic support
*
*********************************************************************/
static int get_adj2_adjusted_value(u8 max_win_size, u32 *adj2_dsx,
adj2_val_t *adj2, u16 ddr16bit_mode)
{
u8 i, adj2_val;
u16 ds, shift;
u8 win_start, win_end, win;
u8 gwin_start, gwin_end, gwin;
for(ds=0; ds < 4; ds++)
{
if ((ds == 2) && (ddr16bit_mode))
break;
shift = ds * 8;
win_start = win_end = win = 0;
gwin_start = gwin_end = gwin = 0;
for(i=0; i < max_win_size; i++)
{
adj2_val = (u8)((adj2_dsx[i] >> shift) & 0xff);
/* if, adj2 val is accepted */
if (adj2_val >= ADJ1_MIN_ACCEPTED_WINDOW)
{
if (win_start == 0) {
win_start = win_end = i;
} else {
win_end++;
/* close the window, if we reach
the end of adj2 range */
if (i == max_win_size)
{
win = win_end - win_start + 1;
if (gwin < win)
{
gwin = win;
gwin_start = win_start;
gwin_end = win_end;
}
DPRINT_LVL2(("DS%d start:%d, end:%d, win:%d\n", ds,win_start,win_end,win));
}
}
}
else
{
/* if (win_start == 0) do nothing */
if (win_start != 0)
{
win = win_end - win_start + 1;
if (gwin < win)
{
gwin = win;
gwin_start = win_start;
gwin_end = win_end;
}
DPRINT_LVL2(("DS%d start:%d, end:%d, win:%d\n", ds,win_start,win_end,win));
win_start = win_end = 0;
}
}
} /* end of for (adj2 for a given ds) */
DPRINT_LVL1(("Read(adj2) DS%d start:%d, end:%d, win:%d\n", ds, gwin_start, gwin_end, gwin));
/* check for adj2 range size */
if (gwin < ADJ2_MIN_ACCEPTED_RANGE)
{
DPRINT_LVL0(("Error - DDR Training results\n"));
return -1;
}
else if (gwin < ADJ2_ACCEPTED_RANGE)
{
DPRINT_LVL0(("\nWARNING - DDR Training results may be Marginal\n"));
}
switch (ds)
{
case 0 :
adj2->ds0.inx = (gwin_start + gwin_end) >> 1;
adj2->ds0.gWin = adj2_dsx[adj2->ds0.inx] & 0xff;
break;
case 1 :
adj2->ds1.inx = (gwin_start + gwin_end) >> 1;
adj2->ds1.gWin = (adj2_dsx[adj2->ds1.inx] & 0xff00) >> 8;
break;
case 2 :
adj2->ds2.inx = (gwin_start + gwin_end) >> 1;
adj2->ds2.gWin = (adj2_dsx[adj2->ds2.inx] & 0xff0000) >> 16;
break;
case 3:
adj2->ds3.inx = (gwin_start + gwin_end) >> 1;
adj2->ds3.gWin = (adj2_dsx[adj2->ds3.inx] & 0xff000000) >> 24;
default:
break;
}
} /* end of for(ds) */
if ((adj2->ds0.gWin < ADJ1_ACCEPTED_WINDOW) ||
(adj2->ds1.gWin < ADJ1_ACCEPTED_WINDOW)) {
goto warning;
}
if (!ddr16bit_mode) {
if ((adj2->ds2.gWin < ADJ1_ACCEPTED_WINDOW) ||
(adj2->ds3.gWin < ADJ1_ACCEPTED_WINDOW)) {
goto warning;
}
}
return 0;
warning:
DPRINT_LVL0(("WARNING - DDR Training results may be Marginal\n"));
DPRINT_LVL1(("Write win: ds0:%d, ds1:%d, ds2:%d, ds3:%d\n", adj2->ds0.gWin,adj2->ds1.gWin,adj2->ds2.gWin,adj2->ds3.gWin));
return 0;
}
/*****************************************************************************
* recalculate_adj1_window:
* re-calculate the win_start and win_end values for the choosen adj2
*
****************************************************************************/
void recalculate_adj1_window(u8 ctrl_lock_val, u8 ddr16bit_mode,
adj2_val_t *adj2, u64 *dword_list,
u32 *word_list)
{
u32 result;
u32 ddr_addr_offset = 0;
u8 write_offset, offset;
u8 ctrl_offset_max;
u8 max_win_size;
/* Reset window pointers */
adj2->ds0.win_start = adj2->ds0.win_end = 0;
adj2->ds1.win_start = adj2->ds1.win_end = 0;
adj2->ds2.win_start = adj2->ds2.win_end = 0;
adj2->ds3.win_start = adj2->ds3.win_end = 0;
adj2->ds0.win = 0;
adj2->ds1.win = 0;
adj2->ds2.win = 0;
adj2->ds3.win = 0;
adj2->ds0.gWin = 0;
adj2->ds1.gWin = 0;
adj2->ds2.gWin = 0;
adj2->ds3.gWin = 0;
ctrl_offset_max = (ctrl_lock_val >= 128) ? 0x3F :
(((ctrl_lock_val/2)-1) & 0x3F); //to make sure it is an odd value
max_win_size = (2*ctrl_offset_max)+1;
enable_resync();
DPRINT_LVL1(("DS0 Read offset: %d\n",adj2->ds0.inx));
DPRINT_LVL1(("DS1 Read offset: %d\n",adj2->ds1.inx));
if (!ddr16bit_mode) {
DPRINT_LVL1(("DS2 Read offset: %d\n",adj2->ds2.inx));
DPRINT_LVL1(("DS3 Read offset: %d\n",adj2->ds3.inx));
}
read_dqs_offset_reg( calOffset(adj2->ds0.inx,ctrl_offset_max),
calOffset(adj2->ds1.inx,ctrl_offset_max),
calOffset(adj2->ds2.inx,ctrl_offset_max),
calOffset(adj2->ds3.inx,ctrl_offset_max));
/* Looping through write range */
for (write_offset = 0; write_offset < max_win_size; write_offset++)
{
offset = calOffset(write_offset,ctrl_offset_max);
write_dq_offset_reg(offset, offset, offset, offset);
result = do_wr_rd_transaction(ddr_addr_offset,dword_list,word_list, ddr16bit_mode);
ddr_addr_offset = (ddr_addr_offset + ADDR_JUMP_SIZE) & (PHYS_SDRAM_SIZE -1);
adj2_dsx_calculate_window(max_win_size,
(result & 0x1)|0x10, write_offset,
adj2->ds0.inx, &adj2->ds0);
adj2_dsx_calculate_window(max_win_size,
(result & 0x2)|0x20, write_offset,
adj2->ds1.inx, &adj2->ds1);
if (!ddr16bit_mode) {
adj2_dsx_calculate_window(max_win_size,
(result & 0x4)|0x40, write_offset,
adj2->ds2.inx, &adj2->ds2);
adj2_dsx_calculate_window(max_win_size,
(result & 0x8)|0x80, write_offset,
adj2->ds3.inx, &adj2->ds3);
}
}
disable_resync();
return;
}
/*****************************************************************************
* ddr_training()
*
****************************************************************************/
static int ddr_training(void)
{
u32 word_list[16] =
{ 0xffffffff, 0x00000000, 0x12345678, 0x9abcdef0,
0xf7f70202, 0xdfdf2020, 0x80407fbf, 0x08040204,
0x8080fdfd, 0x0808dfdf, 0xa5a55a5a, 0x5a5aa5a5,
0xaaaa5555, 0x5555aaaa, 0x0000ffff, 0x0000ffff};
u64 dword_list[16] =
{ 0xffffffff00000000ULL, 0xffffffff00000000ULL,
0x1234567876543210ULL, 0x0123456789abcdefULL,
0xf7f7f7f702020202ULL, 0xdfdfdfdf20202020ULL,
0x804020107fbfdfefULL, 0x0804020110204080ULL,
0x80808080fdfdfdfdULL, 0x08080808dfdfdfdfULL,
0xa5a5a5a55a5a5a5aULL, 0x5a5a5a5aa5a5a5a5ULL,
0xaaaaaaaa55555555ULL, 0x55555555aaaaaaaaULL,
0x00000000ffffffffULL, 0x00000000ffffffffULL
};
u8 result;
u32 ddr_addr_offset = 0;
adj2_val_t adj2;
u16 ddr16bit_mode = 0;
u32 ctrl_lock_val;
u32 ctrl_offset_max;
u32 max_win_size;
u8 read_offset, write_offset, offset;
u8 rds0,rds1,rds2=0,rds3=0;
u8 wds0,wds1,wds2=0,wds3=0;
unsigned int adj2_dsx[128] = { 0 };
DPRINT_LVL0(("\nDDR Training:\n"));
/* check for 16bit mode */
if (readl(0x970000c4) & 0x01000000)
ddr16bit_mode = 1;
adj2.ds0.gWin = 0;
adj2.ds1.gWin = 0;
adj2.ds2.gWin = 0;
adj2.ds3.gWin = 0;
writel(0x80000000, DDR_PHY_DLL_STAT_REG);
ctrl_lock_val = (readl(DDR_PHY_DLL_STAT_REG) >> CTRL_LOCK_SHIFT ) & CTRL_LOCK_MASK ;
DPRINT_LVL1(("CTRL_LOCK_VAL: %d\n", ctrl_lock_val));
ctrl_offset_max = (ctrl_lock_val >= 128) ? 0x3F :
(((ctrl_lock_val/2)-1) & 0x3F); //to make sure it is an odd value
max_win_size = (2*ctrl_offset_max)+1;
/* Looping through read */
for(read_offset = 0; read_offset < max_win_size; read_offset++)
{
putchar('.');
enable_resync();
offset = calOffset(read_offset,ctrl_offset_max);
read_dqs_offset_reg(offset, offset, offset, offset);
DPRINT_LVL1(("\nRead offset: %d, (%c%d)\n", read_offset, (offset & (1 << 6)? '-' : '+'), offset & 0x3f));
/* Reset window pointers */
adj2.ds0.win_start = adj2.ds0.win_end = 0;
adj2.ds1.win_start = adj2.ds1.win_end = 0;
adj2.ds2.win_start = adj2.ds2.win_end = 0;
adj2.ds3.win_start = adj2.ds3.win_end = 0;
adj2.ds0.win = 0;
adj2.ds1.win = 0;
adj2.ds2.win = 0;
adj2.ds3.win = 0;
/* Looping through write range */
for (write_offset = 0; write_offset <= max_win_size; write_offset++)
{
offset = calOffset(write_offset, ctrl_offset_max);
write_dq_offset_reg(offset, offset, offset, offset);
result = do_wr_rd_transaction(ddr_addr_offset,dword_list,word_list, ddr16bit_mode);
ddr_addr_offset = (ddr_addr_offset + ADDR_JUMP_SIZE) & (PHYS_SDRAM_SIZE -1);
adj2_dsx_calculate_window(max_win_size, (result & 0x1) | 0x10, write_offset, read_offset, &adj2.ds0);
adj2_dsx_calculate_window(max_win_size, (result & 0x2) | 0x20, write_offset, read_offset, &adj2.ds1);
if (!ddr16bit_mode) {
adj2_dsx_calculate_window(max_win_size, (result & 0x4) | 0x40, write_offset, read_offset, &adj2.ds2);
adj2_dsx_calculate_window(max_win_size, (result & 0x8) | 0x80, write_offset, read_offset, &adj2.ds3);
}
} /* end of write loop */
disable_resync();
/* store the adj2 win values */
if (ddr16bit_mode) {
adj2_dsx[read_offset] = (adj2.ds1.win << 8) | (adj2.ds0.win);
}
else {
adj2_dsx[read_offset] = (adj2.ds3.win << 24) | (adj2.ds2.win << 16) | (adj2.ds1.win << 8) | (adj2.ds0.win);
}
} //End of read loop
DPRINT_LVL1(("DS0:%d:%d\n", adj2.ds0.inx , adj2.ds0.gWin));
DPRINT_LVL1(("DS1:%d:%d\n", adj2.ds1.inx , adj2.ds1.gWin));
if (!ddr16bit_mode) {
DPRINT_LVL1(("DS2:%d:%d\n", adj2.ds2.inx , adj2.ds2.gWin));
DPRINT_LVL1(("DS3:%d:%d\n", adj2.ds3.inx , adj2.ds3.gWin));
}
/* ds0 */
DPRINT_LIST(0,max_win_size,adj2_dsx);
DPRINT_LIST(1,max_win_size,adj2_dsx);
if (!ddr16bit_mode) {
DPRINT_LIST(2,max_win_size,adj2_dsx);
DPRINT_LIST(3,max_win_size,adj2_dsx);
}
if (get_adj2_adjusted_value(max_win_size, adj2_dsx, &adj2, ddr16bit_mode) < 0)
goto error;
recalculate_adj1_window(ctrl_lock_val, ddr16bit_mode, &adj2, dword_list, word_list);
DPRINT_LVL1(("\nWrite:\nDS0:%d-%d=%d\n", adj2.ds0.gWin_start, adj2.ds0.gWin_end, adj2.ds0.gWin));
DPRINT_LVL1(("DS1:%d-%d=%d\n", adj2.ds1.gWin_start, adj2.ds1.gWin_end, adj2.ds1.gWin));
if (!ddr16bit_mode) {
DPRINT_LVL1(("DS2:%d-%d=%d\n", adj2.ds2.gWin_start, adj2.ds2.gWin_end, adj2.ds2.gWin));
DPRINT_LVL1(("DS3:%d-%d=%d\n", adj2.ds3.gWin_start, adj2.ds3.gWin_end, adj2.ds3.gWin));
}
/* Configure read offset */
rds0 = calOffset(adj2.ds0.inx,ctrl_offset_max);
rds1 = calOffset(adj2.ds1.inx,ctrl_offset_max);
if (!ddr16bit_mode) {
rds2 = calOffset(adj2.ds2.inx,ctrl_offset_max);
rds3 = calOffset(adj2.ds3.inx,ctrl_offset_max);
}
/* configure write offset */
write_offset = (adj2.ds0.gWin_start + adj2.ds0.gWin_end) >> 1;
wds0 = calOffset(write_offset,ctrl_offset_max);
write_offset = (adj2.ds1.gWin_start + adj2.ds1.gWin_end) >> 1;
wds1 = calOffset(write_offset,ctrl_offset_max);
if (!ddr16bit_mode) {
write_offset = (adj2.ds2.gWin_start + adj2.ds2.gWin_end) >> 1;
wds2 = calOffset(write_offset,ctrl_offset_max);
write_offset = (adj2.ds3.gWin_start + adj2.ds3.gWin_end) >> 1;
wds3 = calOffset(write_offset,ctrl_offset_max);
}
enable_resync();
read_dqs_offset_reg(rds0, rds1, rds2, rds3);
write_dq_offset_reg(wds0, wds1, wds2, wds3);
disable_resync();
DPRINT_LVL0(("Done\n"));
DPRINT_LVL0(("Read offset: %02x,%02x,%02x,%02x\n", rds0,rds1,rds2,rds3));
DPRINT_LVL0(("Write offset: %02x,%02x,%02x,%02x\n", wds0,wds1,wds2,wds3));
return 0;
error:
DPRINT_LVL0(("Error\n"));
return 0;
}
/******************************************************************************
* do_wr_rd_transaction
*
*****************************************************************************/
static u8 do_wr_rd_transaction(u32 ddr_address_offset, u64 *dword_list,
u32 *word, u16 ddr16bit_mode)
{
u8 j;
register int reg_0 __asm__ ("r3");
register int reg_1 __asm__ ("r4");
u64 *src, *dst;
u32 read_val;
u8 ret_val = 0;
ddr_address_offset &= ~0x3;
/* Do 64bit wr+rd */
dst = (u64 *)(DDR_BASEADDR+ddr_address_offset);
src = dword_list;
for(j=0; j < 16; j++)
{
__asm__ __volatile__ ("ldmia %0, {%1,%2}" \
: "+r" (src), "=r" (reg_0), "=r" (reg_1) );
__asm__ __volatile__ ("stmia %0, {%1,%2}" \
: "+r" (dst), "=r" (reg_0), "=r" (reg_1) );
if ((*src & 0x000000ff000000ffLL) != (*dst & 0x000000ff000000ffLL))
{
ret_val |= 1;
}
if ((*src & 0x0000ff000000ff00LL) != (*dst & 0x0000ff000000ff00LL))
{
ret_val |= 1 << 1;
}
if (!ddr16bit_mode) {
if ((*src & 0x00ff000000ff0000LL) != (*dst & 0x00ff000000ff0000LL))
{
ret_val |= 1 << 2;
}
if ((*src & 0xff000000ff000000LL) != (*dst & 0xff000000ff000000LL))
{
ret_val |= 1 << 3;
}
}
*dst = 0; //clear location
dst++;
src++;
}
/* Do 32bit wr+rd */
for (j=0; j < 16; j++)
{
*(((volatile u32 *)(DDR_BASEADDR+ddr_address_offset)) + j) = *word;
read_val = *(((volatile u32 *)(DDR_BASEADDR+ddr_address_offset)) + j);
if ((read_val & 0x000000FF) != (*word & 0x000000ff))
{
ret_val |= 1;
}
if ((read_val & 0x0000ff00) != (*word & 0x0000ff00))
{
ret_val |= 1 << 1;
}
if (!ddr16bit_mode) {
if ((read_val & 0x00ff0000) != (*word & 0x00ff0000))
{
ret_val |= 1 << 2;
}
if ((read_val & 0xff000000) != (*word & 0xff000000))
{
ret_val |= 1 << 3;
}
}
*(((volatile u32 *)(DDR_BASEADDR+ddr_address_offset)) + j) = 0; //clear location
word++;
}
return ret_val;
}
device_initcall(ddr_training);