787 lines
23 KiB
C
787 lines
23 KiB
C
/*
|
|
* (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
|
|
}
|