blob: a37bc1a46ec8e1cbdaaba3d43fe019dbe1d02a1d [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 <common.h>
#include <asm/hardware.h>
#include <asm/arch/bsp.h>
//#define DDR_TRAINING_MODE_DEBUG_PRINTS 1
#undef DDR_TRAINING_MODE_DEBUG_PRINTS
#define SZ_1K 0x400
#define SZ_128K (SZ_1K * 128)
#define ADDR_JUMP_SIZE SZ_128K
#define MT_ADDR1_DST (DDR_BASEADDR + 0xff00)
#define MT_ADDR2_DST (DDR_BASEADDR + (PHYS_SDRAM_SIZE >> 1) + 0xff00)
/* Controller register defines used in training */
#define DENALI_WR_DQS_DELAY0 *((volatile u8 *)(DDR_CONFIG_BASEADDR + 0x11B))
#define DENALI_WR_DQS_DELAY1 *((volatile u8 *)(DDR_CONFIG_BASEADDR + 0x11C))
#define DENALI_WR_DQS_DELAY2 *((volatile u8 *)(DDR_CONFIG_BASEADDR + 0x11D))
#define DENALI_WR_DQS_DELAY3 *((volatile u8 *)(DDR_CONFIG_BASEADDR + 0x11E))
#define DENALI_SW_LEVELING_START *((volatile u8 *)(DDR_CONFIG_BASEADDR + 0x278))
#define DENALI_SW_LEVELING_MODE *((volatile u8 *)(DDR_CONFIG_BASEADDR + 0x279))
#define DENALI_SW_LEVELING_LOAD *((volatile u8 *)(DDR_CONFIG_BASEADDR + 0x276))
#define DENALI_DLL_MADJ0 *((volatile u8 *)(DDR_CONFIG_BASEADDR + 0x148))
#define DENALI_DLL_ADJ0 *((volatile u8 *)(DDR_CONFIG_BASEADDR + 0x149))
#define DENALI_DLL_ADJ1_DS0 *((volatile u8 *)(DDR_CONFIG_BASEADDR + 0x14A))
#define DENALI_DLL_ADJ1_DS1 *((volatile u8 *)(DDR_CONFIG_BASEADDR + 0x14E))
#define DENALI_DLL_ADJ1_DS2 *((volatile u8 *)(DDR_CONFIG_BASEADDR + 0x152))
#define DENALI_DLL_ADJ1_DS3 *((volatile u8 *)(DDR_CONFIG_BASEADDR + 0x156))
#define DENALI_DLL_ADJ0_DS0 *((volatile u8 *)(DDR_CONFIG_BASEADDR + 0x149))
#define DENALI_DLL_ADJ0_DS1 *((volatile u8 *)(DDR_CONFIG_BASEADDR + 0x14D))
#define DENALI_DLL_ADJ0_DS2 *((volatile u8 *)(DDR_CONFIG_BASEADDR + 0x151))
#define DENALI_DLL_ADJ0_DS3 *((volatile u8 *)(DDR_CONFIG_BASEADDR + 0x155))
#define DENALI_DLL_ADJ3_DS0 *((volatile u8 *)(DDR_CONFIG_BASEADDR + 0x159))
#define DENALI_DLL_ADJ3_DS1 *((volatile u8 *)(DDR_CONFIG_BASEADDR + 0x15D))
#define DENALI_DLL_ADJ3_DS2 *((volatile u8 *)(DDR_CONFIG_BASEADDR + 0x161))
#define DENALI_DLL_ADJ3_DS3 *((volatile u8 *)(DDR_CONFIG_BASEADDR + 0x165))
static u8 do_wr_rd_transaction(u32 ddr_addr_offset, u64*, u32*, u16 mode);
static int mdma_test(u64 *);
extern void arm_write64(u64 data,volatile u64 *p);
extern int mdma_memcpy(void *src, void *dst, int len, unsigned int *crc);
extern int serial_init(void);
extern void serial_puts(const char *s);
extern void serial_putc(const char c);
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;
#ifdef DDR_TRAINING_MODE_DEBUG_PRINTS
/* use to print only byte */
static char *simple_itoa1(unsigned int i, char *s)
{
char local[5];
char *p = &local[4];
*p-- = '\0';
i = i & 0xff;
do {
*p-- = '0' + i % 10;
i /= 10;
} while (i > 0);
memcpy(s, p+1, 4);
return s;
}
#endif
static void adj2_dsx_calculate_window(u8 result, u8 dll_val, u8 dqs_index, adj2_ds_t *adj2)
{
u8 win;
#ifdef DDR_TRAINING_MODE_DEBUG_PRINTS
char s[5];
u8 dsx = 0;
#endif
if (!result) /* success */
{
/* its first time success, start counting from here */
if (adj2->win_start == 0)
{
adj2->win_start = adj2->win_end = dll_val;
}
else {
adj2->win_end++;
/* close the window, if we reach the end point of adj1 range */
if (dll_val == ADJ1_MAX_VAL) {
win = (adj2->win_end - adj2->win_start) + 1;
if (adj2->win < win)
adj2->win = 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 = dqs_index;
}
}
}
}
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;
#ifdef DDR_TRAINING_MODE_DEBUG_PRINTS
serial_putc('\n');
if (result & 0x1)
dsx = 0;
else if (result & 0x2)
dsx = 1;
else if (result & 0x4)
dsx = 2;
else if (result & 0x8)
dsx = 3;
serial_putc('[');
serial_puts("ds");
serial_puts(simple_itoa1(dsx,s));
serial_putc(':');
serial_puts(simple_itoa1(adj2->win_start,s));
serial_putc('-');
serial_puts(simple_itoa1(adj2->win_end,s));
serial_putc('=');
serial_puts(simple_itoa1(win,s));
serial_putc(']');
#endif
if (adj2->win < win)
{
adj2->win = 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 = dqs_index;
}
/* reset window pointer to measure next window */
adj2->win_start = adj2->win_end = 0;
}
} /* failure */
return;
}
/* for basing on adj2 min value logic support */
static int get_adj2_adjusted_value(unsigned int *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;
#ifdef DDR_TRAINING_MODE_DEBUG_PRINTS
unsigned char s[5];
#endif
for(ds=0; ds < 4; ds++) {
if ((ds == 2) && (ddr16bit_mode))
goto done;
shift = ds * 8;
win_start = win_end = win = 0;
gwin_start = gwin_end = gwin = 0;
#ifdef DDR_TRAINING_MODE_DEBUG_PRINTS
serial_puts("ds");
serial_puts(simple_itoa1(ds, s));
serial_putc('\n');
#endif
for(i=ADJ2_MIN_VAL; i <= ADJ2_MAX_VAL; 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 == ADJ2_MAX_VAL) {
win = win_end - win_start + 1;
if (gwin < win) {
gwin = win;
gwin_start = win_start;
gwin_end = win_end;
}
#ifdef DDR_TRAINING_MODE_DEBUG_PRINTS
serial_puts(simple_itoa1(win_start, s));
serial_putc('-');
serial_puts(simple_itoa1(win_end, s));
serial_putc('=');
serial_puts(simple_itoa1(win, s));
serial_putc('\n');
#endif
}
}
}
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;
}
#ifdef DDR_TRAINING_MODE_DEBUG_PRINTS
serial_puts(simple_itoa1(win_start, s));
serial_putc('-');
serial_puts(simple_itoa1(win_end, s));
serial_putc('=');
serial_puts(simple_itoa1(win, s));
serial_putc('\n');
#endif
win_start = win_end = 0;
}
}
} /* end of for (adj2 for a given ds) */
/* check for adj2 range size */
if (gwin < ADJ2_MIN_ACCEPTED_RANGE) {
return -1;
}
else if (gwin < ADJ2_ACCEPTED_RANGE) {
serial_puts("\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) */
done:
if (ddr16bit_mode) {
if ((adj2->ds0.gWin < ADJ1_ACCEPTED_WINDOW) || (adj2->ds1.gWin < ADJ1_ACCEPTED_WINDOW)) {
goto warning;
}
}
else {
if ((adj2->ds0.gWin < ADJ1_ACCEPTED_WINDOW) || (adj2->ds1.gWin < ADJ1_ACCEPTED_WINDOW) ||
(adj2->ds2.gWin < ADJ1_ACCEPTED_WINDOW) || (adj2->ds3.gWin < ADJ1_ACCEPTED_WINDOW)) {
goto warning;
}
}
return 0;
warning:
serial_puts("\nWARNING - DDR Training results may be Marginal\n");
return 0;
}
/* re-calculate the win_start and win_end values for the choosen adj2 */
void recalculate_adj1_window(u8 ddr16bit_mode, adj2_val_t *adj2,
u64 *dword_list, u32 *word_list)
{
u8 dll_val;
u8 ds;
u32 result;
u32 dqs_index;
u32 ddr_addr_offset = 0;
u64 dqs_value;
/* 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;
for(ds = 0; ds < 4; ds++) {
if ((ds == 2) && (ddr16bit_mode))
return;
if (ds == 0)
dqs_index = adj2->ds0.inx;
else if (ds == 1)
dqs_index = adj2->ds1.inx;
else if (ds == 2)
dqs_index = adj2->ds2.inx;
else if (ds == 3)
dqs_index = adj2->ds3.inx;
/* Configure the WRLVL_DELAY_X values */
if (ddr16bit_mode) {
dqs_value = __le64_to_cpu((*(volatile u64*)(DENALI_CTL_35_DATA))) & 0xffffff0000ffffffULL;
dqs_value |= (u64)(((u64)dqs_index << 24) | ((u64)dqs_index << 32) );
}
else {
dqs_value = __le64_to_cpu((*(volatile u64*)(DENALI_CTL_35_DATA))) & 0xff00000000ffffffULL;
dqs_value |= (u64)(((u64)dqs_index << 24) | ((u64)dqs_index << 32) | ((u64)dqs_index << 40) | ((u64)dqs_index << 48));
}
arm_write64(dqs_value, (volatile u64 *)DENALI_CTL_35_DATA); //should be 64bit write
//set sw leveling mode
*(volatile u64*)(DENALI_CTL_67_DATA) = __cpu_to_le64((u64)DENALI_CTL_67_VAL_CFG1 | (1LL << 8));
//sw leveling load
*(volatile u64*)(DENALI_CTL_66_DATA) = __cpu_to_le64((u64)DENALI_CTL_66_VAL_CFG1 | (1LL << 48));
//reset sw leveling mode
*(volatile u64*)(DENALI_CTL_67_DATA) = __cpu_to_le64((u64)DENALI_CTL_67_VAL_CFG1);
/* Looping through ADJ_1 range */
for (dll_val = ADJ1_MIN_VAL; dll_val <= ADJ1_MAX_VAL; dll_val++)
{
/* Configure dll write click adj-1 values */
DENALI_DLL_ADJ1_DS0 = (u8)dll_val;
DENALI_DLL_ADJ1_DS1 = (u8)dll_val;
if (!ddr16bit_mode) {
DENALI_DLL_ADJ1_DS2 = (u8)dll_val;
DENALI_DLL_ADJ1_DS3 = (u8)dll_val;
}
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);
if (ds == 0)
adj2_dsx_calculate_window((result & 0x1), dll_val, adj2->ds0.inx, &adj2->ds0);
else if (ds == 1)
adj2_dsx_calculate_window((result & 0x2), dll_val, adj2->ds1.inx, &adj2->ds1);
else if (ds == 2)
adj2_dsx_calculate_window((result & 0x4), dll_val, adj2->ds2.inx, &adj2->ds2);
else if (ds == 3)
adj2_dsx_calculate_window((result & 0x8), dll_val, adj2->ds3.inx, &adj2->ds3);
} /* for loop from dll min to max (ADJ_1) */
} /* for each ds */
return;
}
/* find the a bigger dll clk window for the given dqs value */
/* find a dll wr clk window for a given dqs_index,
* this function updates the global dll and dqs values, if its finds any bigger window
* than previous window, and then finally it configures the identified dqs and dll values
* to DDR controller */
void start_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 dll_val;
u8 result;
u8 dqs_index;
u32 ddr_addr_offset = 0;
u64 dqs_value;
adj2_val_t adj2;
u16 ddr16bit_mode = 0;
unsigned char sb[] = "\nDDR Training";
unsigned char sd[] = "Done";
unsigned char sf[] = "Fail";
#ifdef DDR_TRAINING_MODE_DEBUG_PRINTS
unsigned char adj2_s[] = "\nAdj2:";
char s[5];
u8 i;
#endif
unsigned int adj2_dsx[256];
/* Init of UART will be done later through main init sequence,
* so doing init twice does it make any harm????, I think NO */
serial_init();
serial_puts(sb);
/* check for 16bit mode */
if(__le64_to_cpu(*(volatile u64*)(DENALI_CTL_18_DATA)) & 0x0000000001000000ULL)
ddr16bit_mode = 1;
adj2.ds0.gWin = 0;
adj2.ds1.gWin = 0;
adj2.ds2.gWin = 0;
adj2.ds3.gWin = 0;
/* Looping through ADJ_2 range */
for(dqs_index = ADJ2_MIN_VAL; dqs_index <= ADJ2_MAX_VAL; dqs_index++)
{
#ifdef DDR_TRAINING_MODE_DEBUG_PRINTS
serial_puts(adj2_s);
serial_puts(simple_itoa1(dqs_index, s));
serial_putc(':');
#endif
serial_putc('.');
/* Configure the WRLVL_DELAY_X values */
if (ddr16bit_mode) {
dqs_value = __le64_to_cpu((*(volatile u64*)(DENALI_CTL_35_DATA))) & 0xffffff0000ffffffULL;
dqs_value |= (u64)(((u64)dqs_index << 24) | ((u64)dqs_index << 32) );
}
else {
dqs_value = __le64_to_cpu((*(volatile u64*)(DENALI_CTL_35_DATA))) & 0xff00000000ffffffULL;
dqs_value |= (u64)(((u64)dqs_index << 24) | ((u64)dqs_index << 32) | ((u64)dqs_index << 40) | ((u64)dqs_index << 48));
}
arm_write64(dqs_value, (volatile u64 *)DENALI_CTL_35_DATA); //should be 64bit write
//set sw leveling mode
*(volatile u64*)(DENALI_CTL_67_DATA) = __cpu_to_le64((u64)DENALI_CTL_67_VAL_CFG1 | (1LL << 8));
//sw leveling load
*(volatile u64*)(DENALI_CTL_66_DATA) = __cpu_to_le64((u64)DENALI_CTL_66_VAL_CFG1 | (1LL << 48));
//reset sw leveling mode
*(volatile u64*)(DENALI_CTL_67_DATA) = __cpu_to_le64((u64)DENALI_CTL_67_VAL_CFG1);
/* 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 ADJ_1 range */
for (dll_val = ADJ1_MIN_VAL; dll_val <= ADJ1_MAX_VAL; dll_val++)
{
/* Configure dll write click adj-1 values */
DENALI_DLL_ADJ1_DS0 = (u8)dll_val;
DENALI_DLL_ADJ1_DS1 = (u8)dll_val;
if (!ddr16bit_mode) {
DENALI_DLL_ADJ1_DS2 = (u8)dll_val;
DENALI_DLL_ADJ1_DS3 = (u8)dll_val;
}
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((result & 0x1), dll_val, dqs_index, &adj2.ds0);
adj2_dsx_calculate_window((result & 0x2), dll_val, dqs_index, &adj2.ds1);
if (!ddr16bit_mode) {
adj2_dsx_calculate_window((result & 0x4), dll_val, dqs_index, &adj2.ds2);
adj2_dsx_calculate_window((result & 0x8), dll_val, dqs_index, &adj2.ds3);
}
} /* for loop from dll min to max (ADJ_1) */
/* store the adj2 win values */
if (ddr16bit_mode)
adj2_dsx[dqs_index] = (adj2.ds1.win << 8) | (adj2.ds0.win);
else
adj2_dsx[dqs_index] = (adj2.ds3.win << 24) | (adj2.ds2.win << 16) | (adj2.ds1.win << 8) | (adj2.ds0.win);
} //End of ADJ_2
#ifdef DDR_TRAINING_MODE_DEBUG_PRINTS
serial_putc('\n');
serial_puts(simple_itoa1(adj2.ds0.gWin,s));
serial_putc('-');
serial_puts(simple_itoa1(adj2.ds0.inx,s));
serial_putc('\n');
serial_puts(simple_itoa1(adj2.ds1.gWin,s));
serial_putc('-');
serial_puts(simple_itoa1(adj2.ds1.inx,s));
serial_putc('\n');
if (!ddr16bit_mode) {
serial_puts(simple_itoa1(adj2.ds2.gWin,s));
serial_putc('-');
serial_puts(simple_itoa1(adj2.ds2.inx,s));
serial_putc('\n');
serial_puts(simple_itoa1(adj2.ds3.gWin,s));
serial_putc('-');
serial_puts(simple_itoa1(adj2.ds3.inx,s));
serial_putc('-');
}
serial_putc('\n');
/* ds0 */
for (i=ADJ2_MIN_VAL; i <= ADJ2_MAX_VAL; i++) {
serial_puts(simple_itoa1((adj2_dsx[i] & 0xff),s));
serial_putc(',');
}
serial_putc('\n');
/* ds1 */
serial_putc('\n');
for (i=ADJ2_MIN_VAL; i <= ADJ2_MAX_VAL; i++) {
serial_puts(simple_itoa1((adj2_dsx[i] & 0xff00) >> 8,s));
serial_putc(',');
}
serial_putc('\n');
if (!ddr16bit_mode) {
/* ds2 */
serial_putc('\n');
for (i=ADJ2_MIN_VAL; i <= ADJ2_MAX_VAL; i++) {
serial_puts(simple_itoa1((adj2_dsx[i] & 0xff0000) >> 16,s));
serial_putc(',');
}
serial_putc('\n');
/* ds3 */
serial_putc('\n');
for (i=ADJ2_MIN_VAL; i <= ADJ2_MAX_VAL; i++) {
serial_puts(simple_itoa1((adj2_dsx[i] & 0xff000000) >> 24,s));
serial_putc(',');
}
serial_putc('\n');
}
#endif
if (get_adj2_adjusted_value(adj2_dsx, &adj2, ddr16bit_mode) < 0)
goto error;
#ifdef DDR_TRAINING_MODE_DEBUG_PRINTS
serial_puts(simple_itoa1(adj2.ds0.gWin,s));
serial_putc('-');
serial_puts(simple_itoa1(adj2.ds0.inx,s));
serial_putc('\n');
serial_puts(simple_itoa1(adj2.ds1.gWin,s));
serial_putc('-');
serial_puts(simple_itoa1(adj2.ds1.inx,s));
serial_putc('\n');
if (!ddr16bit_mode) {
serial_puts(simple_itoa1(adj2.ds2.gWin,s));
serial_putc('-');
serial_puts(simple_itoa1(adj2.ds2.inx,s));
serial_putc('\n');
serial_puts(simple_itoa1(adj2.ds3.gWin,s));
serial_putc('-');
serial_puts(simple_itoa1(adj2.ds3.inx,s));
serial_putc('-');
}
#endif
recalculate_adj1_window(ddr16bit_mode, &adj2, dword_list, word_list);
/* Configure the WRLVL_DELAY_X values (ADJ_2) */
if (ddr16bit_mode) {
dqs_value = __le64_to_cpu((*(volatile u64*)(DENALI_CTL_35_DATA))) & 0xffffff0000ffffffULL;
dqs_value |= (u64)(((u64)(adj2.ds0.inx) << 24) |
((u64)(adj2.ds1.inx) << 32));
}
else {
dqs_value = __le64_to_cpu((*(volatile u64*)(DENALI_CTL_35_DATA))) & 0xff00000000ffffffULL;
dqs_value |= (u64)(((u64)(adj2.ds0.inx) << 24) |
((u64)(adj2.ds1.inx) << 32) |
((u64)(adj2.ds2.inx) << 40) |
((u64)(adj2.ds3.inx) << 48));
}
arm_write64(dqs_value, (volatile u64 *)DENALI_CTL_35_DATA); //should be 64bit write
//set sw leveling mode
*(volatile u64*)(DENALI_CTL_67_DATA) = __cpu_to_le64((u64)DENALI_CTL_67_VAL_CFG1 | (1LL << 8));
//sw leveling load
*(volatile u64*)(DENALI_CTL_66_DATA) = __cpu_to_le64((u64)DENALI_CTL_66_VAL_CFG1 | (1LL << 48));
//reset sw leveling mode
*(volatile u64*)(DENALI_CTL_67_DATA) = __cpu_to_le64((u64)DENALI_CTL_67_VAL_CFG1);
/* Configure wr_clk ADJ_1 */
DENALI_DLL_ADJ1_DS0 = (u8)((adj2.ds0.gWin_start + adj2.ds0.gWin_end) >> 1);
DENALI_DLL_ADJ1_DS1 = (u8)((adj2.ds1.gWin_start + adj2.ds1.gWin_end) >> 1);
if (!ddr16bit_mode) {
DENALI_DLL_ADJ1_DS2 = (u8)((adj2.ds2.gWin_start + adj2.ds2.gWin_end) >> 1);
DENALI_DLL_ADJ1_DS3 = (u8)((adj2.ds3.gWin_start + adj2.ds3.gWin_end) >> 1);
}
/* training verification using MDMA transfers */
if (mdma_test(dword_list))
{
goto error;
}
serial_puts(sd);
return;
error:
serial_puts(sf);
//watch dog reset
//*(volatile unsigned int *)TIMER_WDT_HIGH_BOUND = 1;
//*(volatile unsigned int *)TIMER_WDT_CONTROL = 1;
while(1);
return;
}
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 = __cpu_to_le64(0); //clear location
dst++;
src++;
}
/* Do 32bit wr+rd */
for (j=0; j < 16; j++)
{
*(((volatile u32 *)(DDR_BASEADDR+ddr_address_offset)) + j) = __cpu_to_le32(*word);
read_val = __le32_to_cpu(*(((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) = __cpu_to_le32(0); //clear location
word++;
ddr_address_offset = (ddr_address_offset + ADDR_JUMP_SIZE) & (PHYS_SDRAM_SIZE -1);
}
return ret_val;
}
/*
* On success returns 0
*/
static int mdma_test(u64 *dword_list)
{
int ii,j;
unsigned int *mdma_data = (u32 *)0x81000000;
unsigned int *ddr_dst;
unsigned int data_len[] = {1024, 1032, 1048, 1064};
/* init mdma data, at this point we are sure that 32bit wr/rd operations are good */
u32 *dataPtr = (u32 *)dword_list;
for(j=0; j < 9; j++)
{
for(ii=0; ii < 32; ii++)
mdma_data[32*j + ii] = __cpu_to_le32(dataPtr[ii]);
}
for(j=0; j < 4; j++)
{
ddr_dst = (unsigned int *)((j % 2) ? MT_ADDR1_DST : MT_ADDR2_DST);
dataPtr = mdma_data;
mdma_memcpy((void *)mdma_data, (void *)ddr_dst, data_len[j], NULL);
for(ii=0; ii < data_len[j] >> 2; ii++)
if (__le32_to_cpu(*dataPtr++) != __le32_to_cpu(*ddr_dst++))
return 1;
}
return 0; //MDMA test pass
}