/*
* Copyright Altera Corporation ( C ) 2012 - 2015
*
* SPDX - License - Identifier : BSD - 3 - Clause
*/
# include <common.h>
# include <asm/io.h>
# include <asm/arch/sdram.h>
# include <errno.h>
# include "sequencer.h"
static struct socfpga_sdr_rw_load_manager * sdr_rw_load_mgr_regs =
( struct socfpga_sdr_rw_load_manager * )
( SDR_PHYGRP_RWMGRGRP_ADDRESS | 0x800 ) ;
static struct socfpga_sdr_rw_load_jump_manager * sdr_rw_load_jump_mgr_regs =
( struct socfpga_sdr_rw_load_jump_manager * )
( SDR_PHYGRP_RWMGRGRP_ADDRESS | 0xC00 ) ;
static struct socfpga_sdr_reg_file * sdr_reg_file =
( struct socfpga_sdr_reg_file * ) SDR_PHYGRP_REGFILEGRP_ADDRESS ;
static struct socfpga_sdr_scc_mgr * sdr_scc_mgr =
( struct socfpga_sdr_scc_mgr * )
( SDR_PHYGRP_SCCGRP_ADDRESS | 0xe00 ) ;
static struct socfpga_phy_mgr_cmd * phy_mgr_cmd =
( struct socfpga_phy_mgr_cmd * ) SDR_PHYGRP_PHYMGRGRP_ADDRESS ;
static struct socfpga_phy_mgr_cfg * phy_mgr_cfg =
( struct socfpga_phy_mgr_cfg * )
( SDR_PHYGRP_PHYMGRGRP_ADDRESS | 0x40 ) ;
static struct socfpga_data_mgr * data_mgr =
( struct socfpga_data_mgr * ) SDR_PHYGRP_DATAMGRGRP_ADDRESS ;
static struct socfpga_sdr_ctrl * sdr_ctrl =
( struct socfpga_sdr_ctrl * ) SDR_CTRLGRP_ADDRESS ;
const struct socfpga_sdram_rw_mgr_config * rwcfg ;
const struct socfpga_sdram_io_config * iocfg ;
const struct socfpga_sdram_misc_config * misccfg ;
# define DELTA_D 1
/*
* In order to reduce ROM size , most of the selectable calibration steps are
* decided at compile time based on the user ' s calibration mode selection ,
* as captured by the STATIC_CALIB_STEPS selection below .
*
* However , to support simulation - time selection of fast simulation mode , where
* we skip everything except the bare minimum , we need a few of the steps to
* be dynamic . In those cases , we either use the DYNAMIC_CALIB_STEPS for the
* check , which is based on the rtl - supplied value , or we dynamically compute
* the value to use based on the dynamically - chosen calibration mode
*/
# define DLEVEL 0
# define STATIC_IN_RTL_SIM 0
# define STATIC_SKIP_DELAY_LOOPS 0
# define STATIC_CALIB_STEPS (STATIC_IN_RTL_SIM | CALIB_SKIP_FULL_TEST | \
STATIC_SKIP_DELAY_LOOPS )
/* calibration steps requested by the rtl */
u16 dyn_calib_steps ;
/*
* To make CALIB_SKIP_DELAY_LOOPS a dynamic conditional option
* instead of static , we use boolean logic to select between
* non - skip and skip values
*
* The mask is set to include all bits when not - skipping , but is
* zero when skipping
*/
u16 skip_delay_mask ; /* mask off bits when skipping/not-skipping */
# define SKIP_DELAY_LOOP_VALUE_OR_ZERO(non_skip_value) \
( ( non_skip_value ) & skip_delay_mask )
struct gbl_type * gbl ;
struct param_type * param ;
static void set_failing_group_stage ( u32 group , u32 stage ,
u32 substage )
{
/*
* Only set the global stage if there was not been any other
* failing group
*/
if ( gbl - > error_stage = = CAL_STAGE_NIL ) {
gbl - > error_substage = substage ;
gbl - > error_stage = stage ;
gbl - > error_group = group ;
}
}
static void reg_file_set_group ( u16 set_group )
{
clrsetbits_le32 ( & sdr_reg_file - > cur_stage , 0xffff0000 , set_group < < 16 ) ;
}
static void reg_file_set_stage ( u8 set_stage )
{
clrsetbits_le32 ( & sdr_reg_file - > cur_stage , 0xffff , set_stage & 0xff ) ;
}
static void reg_file_set_sub_stage ( u8 set_sub_stage )
{
set_sub_stage & = 0xff ;
clrsetbits_le32 ( & sdr_reg_file - > cur_stage , 0xff00 , set_sub_stage < < 8 ) ;
}
/**
* phy_mgr_initialize ( ) - Initialize PHY Manager
*
* Initialize PHY Manager .
*/
static void phy_mgr_initialize ( void )
{
u32 ratio ;
debug ( " %s:%d \n " , __func__ , __LINE__ ) ;
/* Calibration has control over path to memory */
/*
* In Hard PHY this is a 2 - bit control :
* 0 : AFI Mux Select
* 1 : DDIO Mux Select
*/
writel ( 0x3 , & phy_mgr_cfg - > mux_sel ) ;
/* USER memory clock is not stable we begin initialization */
writel ( 0 , & phy_mgr_cfg - > reset_mem_stbl ) ;
/* USER calibration status all set to zero */
writel ( 0 , & phy_mgr_cfg - > cal_status ) ;
writel ( 0 , & phy_mgr_cfg - > cal_debug_info ) ;
/* Init params only if we do NOT skip calibration. */
if ( ( dyn_calib_steps & CALIB_SKIP_ALL ) = = CALIB_SKIP_ALL )
return ;
ratio = rwcfg - > mem_dq_per_read_dqs /
rwcfg - > mem_virtual_groups_per_read_dqs ;
param - > read_correct_mask_vg = ( 1 < < ratio ) - 1 ;
param - > write_correct_mask_vg = ( 1 < < ratio ) - 1 ;
param - > read_correct_mask = ( 1 < < rwcfg - > mem_dq_per_read_dqs ) - 1 ;
param - > write_correct_mask = ( 1 < < rwcfg - > mem_dq_per_write_dqs ) - 1 ;
}
/**
* set_rank_and_odt_mask ( ) - Set Rank and ODT mask
* @ rank : Rank mask
* @ odt_mode : ODT mode , OFF or READ_WRITE
*
* Set Rank and ODT mask ( On - Die Termination ) .
*/
static void set_rank_and_odt_mask ( const u32 rank , const u32 odt_mode )
{
u32 odt_mask_0 = 0 ;
u32 odt_mask_1 = 0 ;
u32 cs_and_odt_mask ;
if ( odt_mode = = RW_MGR_ODT_MODE_OFF ) {
odt_mask_0 = 0x0 ;
odt_mask_1 = 0x0 ;
} else { /* RW_MGR_ODT_MODE_READ_WRITE */
switch ( rwcfg - > mem_number_of_ranks ) {
case 1 : /* 1 Rank */
/* Read: ODT = 0 ; Write: ODT = 1 */
odt_mask_0 = 0x0 ;
odt_mask_1 = 0x1 ;
break ;
case 2 : /* 2 Ranks */
if ( rwcfg - > mem_number_of_cs_per_dimm = = 1 ) {
/*
* - Dual - Slot , Single - Rank ( 1 CS per DIMM )
* OR
* - RDIMM , 4 total CS ( 2 CS per DIMM , 2 DIMM )
*
* Since MEM_NUMBER_OF_RANKS is 2 , they
* are both single rank with 2 CS each
* ( special for RDIMM ) .
*
* Read : Turn on ODT on the opposite rank
* Write : Turn on ODT on all ranks
*/
odt_mask_0 = 0x3 & ~ ( 1 < < rank ) ;
odt_mask_1 = 0x3 ;
} else {
/*
* - Single - Slot , Dual - Rank ( 2 CS per DIMM )
*
* Read : Turn on ODT off on all ranks
* Write : Turn on ODT on active rank
*/
odt_mask_0 = 0x0 ;
odt_mask_1 = 0x3 & ( 1 < < rank ) ;
}
break ;
case 4 : /* 4 Ranks */
/* Read:
* - - - - - - - - - - + - - - - - - - - - - - - - - - - - - - - - - - +
* | ODT |
* Read From + - - - - - - - - - - - - - - - - - - - - - - - +
* Rank | 3 | 2 | 1 | 0 |
* - - - - - - - - - - + - - - - - + - - - - - + - - - - - + - - - - - +
* 0 | 0 | 1 | 0 | 0 |
* 1 | 1 | 0 | 0 | 0 |
* 2 | 0 | 0 | 0 | 1 |
* 3 | 0 | 0 | 1 | 0 |
* - - - - - - - - - - + - - - - - + - - - - - + - - - - - + - - - - - +
*
* Write :
* - - - - - - - - - - + - - - - - - - - - - - - - - - - - - - - - - - +
* | ODT |
* Write To + - - - - - - - - - - - - - - - - - - - - - - - +
* Rank | 3 | 2 | 1 | 0 |
* - - - - - - - - - - + - - - - - + - - - - - + - - - - - + - - - - - +
* 0 | 0 | 1 | 0 | 1 |
* 1 | 1 | 0 | 1 | 0 |
* 2 | 0 | 1 | 0 | 1 |
* 3 | 1 | 0 | 1 | 0 |
* - - - - - - - - - - + - - - - - + - - - - - + - - - - - + - - - - - +
*/
switch ( rank ) {
case 0 :
odt_mask_0 = 0x4 ;
odt_mask_1 = 0x5 ;
break ;
case 1 :
odt_mask_0 = 0x8 ;
odt_mask_1 = 0xA ;
break ;
case 2 :
odt_mask_0 = 0x1 ;
odt_mask_1 = 0x5 ;
break ;
case 3 :
odt_mask_0 = 0x2 ;
odt_mask_1 = 0xA ;
break ;
}
break ;
}
}
cs_and_odt_mask = ( 0xFF & ~ ( 1 < < rank ) ) |
( ( 0xFF & odt_mask_0 ) < < 8 ) |
( ( 0xFF & odt_mask_1 ) < < 16 ) ;
writel ( cs_and_odt_mask , SDR_PHYGRP_RWMGRGRP_ADDRESS |
RW_MGR_SET_CS_AND_ODT_MASK_OFFSET ) ;
}
/**
* scc_mgr_set ( ) - Set SCC Manager register
* @ off : Base offset in SCC Manager space
* @ grp : Read / Write group
* @ val : Value to be set
*
* This function sets the SCC Manager ( Scan Chain Control Manager ) register .
*/
static void scc_mgr_set ( u32 off , u32 grp , u32 val )
{
writel ( val , SDR_PHYGRP_SCCGRP_ADDRESS | off | ( grp < < 2 ) ) ;
}
/**
* scc_mgr_initialize ( ) - Initialize SCC Manager registers
*
* Initialize SCC Manager registers .
*/
static void scc_mgr_initialize ( void )
{
/*
* Clear register file for HPS . 16 ( 2 ^ 4 ) is the size of the
* full register file in the scc mgr :
* RFILE_DEPTH = 1 + log2 ( MEM_DQ_PER_DQS + 1 + MEM_DM_PER_DQS +
* MEM_IF_READ_DQS_WIDTH - 1 ) ;
*/
int i ;
for ( i = 0 ; i < 16 ; i + + ) {
debug_cond ( DLEVEL = = 1 , " %s:%d: Clearing SCC RFILE index %u \n " ,
__func__ , __LINE__ , i ) ;
scc_mgr_set ( SCC_MGR_HHP_RFILE_OFFSET , 0 , i ) ;
}
}
static void scc_mgr_set_dqdqs_output_phase ( u32 write_group , u32 phase )
{
scc_mgr_set ( SCC_MGR_DQDQS_OUT_PHASE_OFFSET , write_group , phase ) ;
}
static void scc_mgr_set_dqs_bus_in_delay ( u32 read_group , u32 delay )
{
scc_mgr_set ( SCC_MGR_DQS_IN_DELAY_OFFSET , read_group , delay ) ;
}
static void scc_mgr_set_dqs_en_phase ( u32 read_group , u32 phase )
{
scc_mgr_set ( SCC_MGR_DQS_EN_PHASE_OFFSET , read_group , phase ) ;
}
static void scc_mgr_set_dqs_en_delay ( u32 read_group , u32 delay )
{
scc_mgr_set ( SCC_MGR_DQS_EN_DELAY_OFFSET , read_group , delay ) ;
}
static void scc_mgr_set_dqs_io_in_delay ( u32 delay )
{
scc_mgr_set ( SCC_MGR_IO_IN_DELAY_OFFSET , rwcfg - > mem_dq_per_write_dqs ,
delay ) ;
}
static void scc_mgr_set_dq_in_delay ( u32 dq_in_group , u32 delay )
{
scc_mgr_set ( SCC_MGR_IO_IN_DELAY_OFFSET , dq_in_group , delay ) ;
}
static void scc_mgr_set_dq_out1_delay ( u32 dq_in_group , u32 delay )
{
scc_mgr_set ( SCC_MGR_IO_OUT1_DELAY_OFFSET , dq_in_group , delay ) ;
}
static void scc_mgr_set_dqs_out1_delay ( u32 delay )
{
scc_mgr_set ( SCC_MGR_IO_OUT1_DELAY_OFFSET , rwcfg - > mem_dq_per_write_dqs ,
delay ) ;
}
static void scc_mgr_set_dm_out1_delay ( u32 dm , u32 delay )
{
scc_mgr_set ( SCC_MGR_IO_OUT1_DELAY_OFFSET ,
rwcfg - > mem_dq_per_write_dqs + 1 + dm ,
delay ) ;
}
/* load up dqs config settings */
static void scc_mgr_load_dqs ( u32 dqs )
{
writel ( dqs , & sdr_scc_mgr - > dqs_ena ) ;
}
/* load up dqs io config settings */
static void scc_mgr_load_dqs_io ( void )
{
writel ( 0 , & sdr_scc_mgr - > dqs_io_ena ) ;
}
/* load up dq config settings */
static void scc_mgr_load_dq ( u32 dq_in_group )
{
writel ( dq_in_group , & sdr_scc_mgr - > dq_ena ) ;
}
/* load up dm config settings */
static void scc_mgr_load_dm ( u32 dm )
{
writel ( dm , & sdr_scc_mgr - > dm_ena ) ;
}
/**
* scc_mgr_set_all_ranks ( ) - Set SCC Manager register for all ranks
* @ off : Base offset in SCC Manager space
* @ grp : Read / Write group
* @ val : Value to be set
* @ update : If non - zero , trigger SCC Manager update for all ranks
*
* This function sets the SCC Manager ( Scan Chain Control Manager ) register
* and optionally triggers the SCC update for all ranks .
*/
static void scc_mgr_set_all_ranks ( const u32 off , const u32 grp , const u32 val ,
const int update )
{
u32 r ;
for ( r = 0 ; r < rwcfg - > mem_number_of_ranks ;
r + = NUM_RANKS_PER_SHADOW_REG ) {
scc_mgr_set ( off , grp , val ) ;
if ( update | | ( r = = 0 ) ) {
writel ( grp , & sdr_scc_mgr - > dqs_ena ) ;
writel ( 0 , & sdr_scc_mgr - > update ) ;
}
}
}
static void scc_mgr_set_dqs_en_phase_all_ranks ( u32 read_group , u32 phase )
{
/*
* USER although the h / w doesn ' t support different phases per
* shadow register , for simplicity our scc manager modeling
* keeps different phase settings per shadow reg , and it ' s
* important for us to keep them in sync to match h / w .
* for efficiency , the scan chain update should occur only
* once to sr0 .
*/
scc_mgr_set_all_ranks ( SCC_MGR_DQS_EN_PHASE_OFFSET ,
read_group , phase , 0 ) ;
}
static void scc_mgr_set_dqdqs_output_phase_all_ranks ( u32 write_group ,
u32 phase )
{
/*
* USER although the h / w doesn ' t support different phases per
* shadow register , for simplicity our scc manager modeling
* keeps different phase settings per shadow reg , and it ' s
* important for us to keep them in sync to match h / w .
* for efficiency , the scan chain update should occur only
* once to sr0 .
*/
scc_mgr_set_all_ranks ( SCC_MGR_DQDQS_OUT_PHASE_OFFSET ,
write_group , phase , 0 ) ;
}
static void scc_mgr_set_dqs_en_delay_all_ranks ( u32 read_group ,
u32 delay )
{
/*
* In shadow register mode , the T11 settings are stored in
* registers in the core , which are updated by the DQS_ENA
* signals . Not issuing the SCC_MGR_UPD command allows us to
* save lots of rank switching overhead , by calling
* select_shadow_regs_for_update with update_scan_chains
* set to 0.
*/
scc_mgr_set_all_ranks ( SCC_MGR_DQS_EN_DELAY_OFFSET ,
read_group , delay , 1 ) ;
writel ( 0 , & sdr_scc_mgr - > update ) ;
}
/**
* scc_mgr_set_oct_out1_delay ( ) - Set OCT output delay
* @ write_group : Write group
* @ delay : Delay value
*
* This function sets the OCT output delay in SCC manager .
*/
static void scc_mgr_set_oct_out1_delay ( const u32 write_group , const u32 delay )
{
const int ratio = rwcfg - > mem_if_read_dqs_width /
rwcfg - > mem_if_write_dqs_width ;
const int base = write_group * ratio ;
int i ;
/*
* Load the setting in the SCC manager
* Although OCT affects only write data , the OCT delay is controlled
* by the DQS logic block which is instantiated once per read group .
* For protocols where a write group consists of multiple read groups ,
* the setting must be set multiple times .
*/
for ( i = 0 ; i < ratio ; i + + )
scc_mgr_set ( SCC_MGR_OCT_OUT1_DELAY_OFFSET , base + i , delay ) ;
}
/**
* scc_mgr_set_hhp_extras ( ) - Set HHP extras .
*
* Load the fixed setting in the SCC manager HHP extras .
*/
static void scc_mgr_set_hhp_extras ( void )
{
/*
* Load the fixed setting in the SCC manager
* bits : 0 : 0 = 1 ' b1 - DQS bypass
* bits : 1 : 1 = 1 ' b1 - DQ bypass
* bits : 4 : 2 = 3 ' b001 - rfifo_mode
* bits : 6 : 5 = 2 ' b01 - rfifo clock_select
* bits : 7 : 7 = 1 ' b0 - separate gating from ungating setting
* bits : 8 : 8 = 1 ' b0 - separate OE from Output delay setting
*/
const u32 value = ( 0 < < 8 ) | ( 0 < < 7 ) | ( 1 < < 5 ) |
( 1 < < 2 ) | ( 1 < < 1 ) | ( 1 < < 0 ) ;
const u32 addr = SDR_PHYGRP_SCCGRP_ADDRESS |
SCC_MGR_HHP_GLOBALS_OFFSET |
SCC_MGR_HHP_EXTRAS_OFFSET ;
debug_cond ( DLEVEL = = 1 , " %s:%d Setting HHP Extras \n " ,
__func__ , __LINE__ ) ;
writel ( value , addr ) ;
debug_cond ( DLEVEL = = 1 , " %s:%d Done Setting HHP Extras \n " ,
__func__ , __LINE__ ) ;
}
/**
* scc_mgr_zero_all ( ) - Zero all DQS config
*
* Zero all DQS config .
*/
static void scc_mgr_zero_all ( void )
{
int i , r ;
/*
* USER Zero all DQS config settings , across all groups and all
* shadow registers
*/
for ( r = 0 ; r < rwcfg - > mem_number_of_ranks ;
r + = NUM_RANKS_PER_SHADOW_REG ) {
for ( i = 0 ; i < rwcfg - > mem_if_read_dqs_width ; i + + ) {
/*
* The phases actually don ' t exist on a per - rank basis ,
* but there ' s no harm updating them several times , so
* let ' s keep the code simple .
*/
scc_mgr_set_dqs_bus_in_delay ( i , iocfg - > dqs_in_reserve ) ;
scc_mgr_set_dqs_en_phase ( i , 0 ) ;
scc_mgr_set_dqs_en_delay ( i , 0 ) ;
}
for ( i = 0 ; i < rwcfg - > mem_if_write_dqs_width ; i + + ) {
scc_mgr_set_dqdqs_output_phase ( i , 0 ) ;
/* Arria V/Cyclone V don't have out2. */
scc_mgr_set_oct_out1_delay ( i , iocfg - > dqs_out_reserve ) ;
}
}
/* Multicast to all DQS group enables. */
writel ( 0xff , & sdr_scc_mgr - > dqs_ena ) ;
writel ( 0 , & sdr_scc_mgr - > update ) ;
}
/**
* scc_set_bypass_mode ( ) - Set bypass mode and trigger SCC update
* @ write_group : Write group
*
* Set bypass mode and trigger SCC update .
*/
static void scc_set_bypass_mode ( const u32 write_group )
{
/* Multicast to all DQ enables. */
writel ( 0xff , & sdr_scc_mgr - > dq_ena ) ;
writel ( 0xff , & sdr_scc_mgr - > dm_ena ) ;
/* Update current DQS IO enable. */
writel ( 0 , & sdr_scc_mgr - > dqs_io_ena ) ;
/* Update the DQS logic. */
writel ( write_group , & sdr_scc_mgr - > dqs_ena ) ;
/* Hit update. */
writel ( 0 , & sdr_scc_mgr - > update ) ;
}
/**
* scc_mgr_load_dqs_for_write_group ( ) - Load DQS settings for Write Group
* @ write_group : Write group
*
* Load DQS settings for Write Group , do not trigger SCC update .
*/
static void scc_mgr_load_dqs_for_write_group ( const u32 write_group )
{
const int ratio = rwcfg - > mem_if_read_dqs_width /
rwcfg - > mem_if_write_dqs_width ;
const int base = write_group * ratio ;
int i ;
/*
* Load the setting in the SCC manager
* Although OCT affects only write data , the OCT delay is controlled
* by the DQS logic block which is instantiated once per read group .
* For protocols where a write group consists of multiple read groups ,
* the setting must be set multiple times .
*/
for ( i = 0 ; i < ratio ; i + + )
writel ( base + i , & sdr_scc_mgr - > dqs_ena ) ;
}
/**
* scc_mgr_zero_group ( ) - Zero all configs for a group
*
* Zero DQ , DM , DQS and OCT configs for a group .
*/
static void scc_mgr_zero_group ( const u32 write_group , const int out_only )
{
int i , r ;
for ( r = 0 ; r < rwcfg - > mem_number_of_ranks ;
r + = NUM_RANKS_PER_SHADOW_REG ) {
/* Zero all DQ config settings. */
for ( i = 0 ; i < rwcfg - > mem_dq_per_write_dqs ; i + + ) {
scc_mgr_set_dq_out1_delay ( i , 0 ) ;
if ( ! out_only )
scc_mgr_set_dq_in_delay ( i , 0 ) ;
}
/* Multicast to all DQ enables. */
writel ( 0xff , & sdr_scc_mgr - > dq_ena ) ;
/* Zero all DM config settings. */
for ( i = 0 ; i < RW_MGR_NUM_DM_PER_WRITE_GROUP ; i + + )
scc_mgr_set_dm_out1_delay ( i , 0 ) ;
/* Multicast to all DM enables. */
writel ( 0xff , & sdr_scc_mgr - > dm_ena ) ;
/* Zero all DQS IO settings. */
if ( ! out_only )
scc_mgr_set_dqs_io_in_delay ( 0 ) ;
/* Arria V/Cyclone V don't have out2. */
scc_mgr_set_dqs_out1_delay ( iocfg - > dqs_out_reserve ) ;
scc_mgr_set_oct_out1_delay ( write_group , iocfg - > dqs_out_reserve ) ;
scc_mgr_load_dqs_for_write_group ( write_group ) ;
/* Multicast to all DQS IO enables (only 1 in total). */
writel ( 0 , & sdr_scc_mgr - > dqs_io_ena ) ;
/* Hit update to zero everything. */
writel ( 0 , & sdr_scc_mgr - > update ) ;
}
}
/*
* apply and load a particular input delay for the DQ pins in a group
* group_bgn is the index of the first dq pin ( in the write group )
*/
static void scc_mgr_apply_group_dq_in_delay ( u32 group_bgn , u32 delay )
{
u32 i , p ;
for ( i = 0 , p = group_bgn ; i < rwcfg - > mem_dq_per_read_dqs ; i + + , p + + ) {
scc_mgr_set_dq_in_delay ( p , delay ) ;
scc_mgr_load_dq ( p ) ;
}
}
/**
* scc_mgr_apply_group_dq_out1_delay ( ) - Apply and load an output delay for the DQ pins in a group
* @ delay : Delay value
*
* Apply and load a particular output delay for the DQ pins in a group .
*/
static void scc_mgr_apply_group_dq_out1_delay ( const u32 delay )
{
int i ;
for ( i = 0 ; i < rwcfg - > mem_dq_per_write_dqs ; i + + ) {
scc_mgr_set_dq_out1_delay ( i , delay ) ;
scc_mgr_load_dq ( i ) ;
}
}
/* apply and load a particular output delay for the DM pins in a group */
static void scc_mgr_apply_group_dm_out1_delay ( u32 delay1 )
{
u32 i ;
for ( i = 0 ; i < RW_MGR_NUM_DM_PER_WRITE_GROUP ; i + + ) {
scc_mgr_set_dm_out1_delay ( i , delay1 ) ;
scc_mgr_load_dm ( i ) ;
}
}
/* apply and load delay on both DQS and OCT out1 */
static void scc_mgr_apply_group_dqs_io_and_oct_out1 ( u32 write_group ,
u32 delay )
{
scc_mgr_set_dqs_out1_delay ( delay ) ;
scc_mgr_load_dqs_io ( ) ;
scc_mgr_set_oct_out1_delay ( write_group , delay ) ;
scc_mgr_load_dqs_for_write_group ( write_group ) ;
}
/**
* scc_mgr_apply_group_all_out_delay_add ( ) - Apply a delay to the entire output side : DQ , DM , DQS , OCT
* @ write_group : Write group
* @ delay : Delay value
*
* Apply a delay to the entire output side : DQ , DM , DQS , OCT .
*/
static void scc_mgr_apply_group_all_out_delay_add ( const u32 write_group ,
const u32 delay )
{
u32 i , new_delay ;
/* DQ shift */
for ( i = 0 ; i < rwcfg - > mem_dq_per_write_dqs ; i + + )
scc_mgr_load_dq ( i ) ;
/* DM shift */
for ( i = 0 ; i < RW_MGR_NUM_DM_PER_WRITE_GROUP ; i + + )
scc_mgr_load_dm ( i ) ;
/* DQS shift */
new_delay = READ_SCC_DQS_IO_OUT2_DELAY + delay ;
if ( new_delay > iocfg - > io_out2_delay_max ) {
debug_cond ( DLEVEL = = 1 ,
" %s:%d (%u, %u) DQS: %u > %d; adding %u to OUT1 \n " ,
__func__ , __LINE__ , write_group , delay , new_delay ,
iocfg - > io_out2_delay_max ,
new_delay - iocfg - > io_out2_delay_max ) ;
new_delay - = iocfg - > io_out2_delay_max ;
scc_mgr_set_dqs_out1_delay ( new_delay ) ;
}
scc_mgr_load_dqs_io ( ) ;
/* OCT shift */
new_delay = READ_SCC_OCT_OUT2_DELAY + delay ;
if ( new_delay > iocfg - > io_out2_delay_max ) {
debug_cond ( DLEVEL = = 1 ,
" %s:%d (%u, %u) DQS: %u > %d; adding %u to OUT1 \n " ,
__func__ , __LINE__ , write_group , delay ,
new_delay , iocfg - > io_out2_delay_max ,
new_delay - iocfg - > io_out2_delay_max ) ;
new_delay - = iocfg - > io_out2_delay_max ;
scc_mgr_set_oct_out1_delay ( write_group , new_delay ) ;
}
scc_mgr_load_dqs_for_write_group ( write_group ) ;
}
/**
* scc_mgr_apply_group_all_out_delay_add ( ) - Apply a delay to the entire output side to all ranks
* @ write_group : Write group
* @ delay : Delay value
*
* Apply a delay to the entire output side ( DQ , DM , DQS , OCT ) to all ranks .
*/
static void
scc_mgr_apply_group_all_out_delay_add_all_ranks ( const u32 write_group ,
const u32 delay )
{
int r ;
for ( r = 0 ; r < rwcfg - > mem_number_of_ranks ;
r + = NUM_RANKS_PER_SHADOW_REG ) {
scc_mgr_apply_group_all_out_delay_add ( write_group , delay ) ;
writel ( 0 , & sdr_scc_mgr - > update ) ;
}
}
/**
* set_jump_as_return ( ) - Return instruction optimization
*
* Optimization used to recover some slots in ddr3 inst_rom could be
* applied to other protocols if we wanted to
*/
static void set_jump_as_return ( void )
{
/*
* To save space , we replace return with jump to special shared
* RETURN instruction so we set the counter to large value so that
* we always jump .
*/
writel ( 0xff , & sdr_rw_load_mgr_regs - > load_cntr0 ) ;
writel ( rwcfg - > rreturn , & sdr_rw_load_jump_mgr_regs - > load_jump_add0 ) ;
}
/**
* delay_for_n_mem_clocks ( ) - Delay for N memory clocks
* @ clocks : Length of the delay
*
* Delay for N memory clocks .
*/
static void delay_for_n_mem_clocks ( const u32 clocks )
{
u32 afi_clocks ;
u16 c_loop ;
u8 inner ;
u8 outer ;
debug ( " %s:%d: clocks=%u ... start \n " , __func__ , __LINE__ , clocks ) ;
/* Scale (rounding up) to get afi clocks. */
afi_clocks = DIV_ROUND_UP ( clocks , misccfg - > afi_rate_ratio ) ;
if ( afi_clocks ) /* Temporary underflow protection */
afi_clocks - - ;
/*
* Note , we don ' t bother accounting for being off a little
* bit because of a few extra instructions in outer loops .
* Note , the loops have a test at the end , and do the test
* before the decrement , and so always perform the loop
* 1 time more than the counter value
*/
c_loop = afi_clocks > > 16 ;
outer = c_loop ? 0xff : ( afi_clocks > > 8 ) ;
inner = outer ? 0xff : afi_clocks ;
/*
* rom instructions are structured as follows :
*
* IDLE_LOOP2 : jnz cntr0 , TARGET_A
* IDLE_LOOP1 : jnz cntr1 , TARGET_B
* return
*
* so , when doing nested loops , TARGET_A is set to IDLE_LOOP2 , and
* TARGET_B is set to IDLE_LOOP2 as well
*
* if we have no outer loop , though , then we can use IDLE_LOOP1 only ,
* and set TARGET_B to IDLE_LOOP1 and we skip IDLE_LOOP2 entirely
*
* a little confusing , but it helps save precious space in the inst_rom
* and sequencer rom and keeps the delays more accurate and reduces
* overhead
*/
if ( afi_clocks < 0x100 ) {
writel ( SKIP_DELAY_LOOP_VALUE_OR_ZERO ( inner ) ,
& sdr_rw_load_mgr_regs - > load_cntr1 ) ;
writel ( rwcfg - > idle_loop1 ,
& sdr_rw_load_jump_mgr_regs - > load_jump_add1 ) ;
writel ( rwcfg - > idle_loop1 , SDR_PHYGRP_RWMGRGRP_ADDRESS |
RW_MGR_RUN_SINGLE_GROUP_OFFSET ) ;
} else {
writel ( SKIP_DELAY_LOOP_VALUE_OR_ZERO ( inner ) ,
& sdr_rw_load_mgr_regs - > load_cntr0 ) ;
writel ( SKIP_DELAY_LOOP_VALUE_OR_ZERO ( outer ) ,
& sdr_rw_load_mgr_regs - > load_cntr1 ) ;
writel ( rwcfg - > idle_loop2 ,
& sdr_rw_load_jump_mgr_regs - > load_jump_add0 ) ;
writel ( rwcfg - > idle_loop2 ,
& sdr_rw_load_jump_mgr_regs - > load_jump_add1 ) ;
do {
writel ( rwcfg - > idle_loop2 ,
SDR_PHYGRP_RWMGRGRP_ADDRESS |
RW_MGR_RUN_SINGLE_GROUP_OFFSET ) ;
} while ( c_loop - - ! = 0 ) ;
}
debug ( " %s:%d clocks=%u ... end \n " , __func__ , __LINE__ , clocks ) ;
}
/**
* rw_mgr_mem_init_load_regs ( ) - Load instruction registers
* @ cntr0 : Counter 0 value
* @ cntr1 : Counter 1 value
* @ cntr2 : Counter 2 value
* @ jump : Jump instruction value
*
* Load instruction registers .
*/
static void rw_mgr_mem_init_load_regs ( u32 cntr0 , u32 cntr1 , u32 cntr2 , u32 jump )
{
u32 grpaddr = SDR_PHYGRP_RWMGRGRP_ADDRESS |
RW_MGR_RUN_SINGLE_GROUP_OFFSET ;
/* Load counters */
writel ( SKIP_DELAY_LOOP_VALUE_OR_ZERO ( cntr0 ) ,
& sdr_rw_load_mgr_regs - > load_cntr0 ) ;
writel ( SKIP_DELAY_LOOP_VALUE_OR_ZERO ( cntr1 ) ,
& sdr_rw_load_mgr_regs - > load_cntr1 ) ;
writel ( SKIP_DELAY_LOOP_VALUE_OR_ZERO ( cntr2 ) ,
& sdr_rw_load_mgr_regs - > load_cntr2 ) ;
/* Load jump address */
writel ( jump , & sdr_rw_load_jump_mgr_regs - > load_jump_add0 ) ;
writel ( jump , & sdr_rw_load_jump_mgr_regs - > load_jump_add1 ) ;
writel ( jump , & sdr_rw_load_jump_mgr_regs - > load_jump_add2 ) ;
/* Execute count instruction */
writel ( jump , grpaddr ) ;
}
/**
* rw_mgr_mem_load_user ( ) - Load user calibration values
* @ fin1 : Final instruction 1
* @ fin2 : Final instruction 2
* @ precharge : If 1 , precharge the banks at the end
*
* Load user calibration values and optionally precharge the banks .
*/
static void rw_mgr_mem_load_user ( const u32 fin1 , const u32 fin2 ,
const int precharge )
{
u32 grpaddr = SDR_PHYGRP_RWMGRGRP_ADDRESS |
RW_MGR_RUN_SINGLE_GROUP_OFFSET ;
u32 r ;
for ( r = 0 ; r < rwcfg - > mem_number_of_ranks ; r + + ) {
/* set rank */
set_rank_and_odt_mask ( r , RW_MGR_ODT_MODE_OFF ) ;
/* precharge all banks ... */
if ( precharge )
writel ( rwcfg - > precharge_all , grpaddr ) ;
/*
* USER Use Mirror - ed commands for odd ranks if address
* mirrorring is on
*/
if ( ( rwcfg - > mem_address_mirroring > > r ) & 0x1 ) {
set_jump_as_return ( ) ;
writel ( rwcfg - > mrs2_mirr , grpaddr ) ;
delay_for_n_mem_clocks ( 4 ) ;
set_jump_as_return ( ) ;
writel ( rwcfg - > mrs3_mirr , grpaddr ) ;
delay_for_n_mem_clocks ( 4 ) ;
set_jump_as_return ( ) ;
writel ( rwcfg - > mrs1_mirr , grpaddr ) ;
delay_for_n_mem_clocks ( 4 ) ;
set_jump_as_return ( ) ;
writel ( fin1 , grpaddr ) ;
} else {
set_jump_as_return ( ) ;
writel ( rwcfg - > mrs2 , grpaddr ) ;
delay_for_n_mem_clocks ( 4 ) ;
set_jump_as_return ( ) ;
writel ( rwcfg - > mrs3 , grpaddr ) ;
delay_for_n_mem_clocks ( 4 ) ;
set_jump_as_return ( ) ;
writel ( rwcfg - > mrs1 , grpaddr ) ;
set_jump_as_return ( ) ;
writel ( fin2 , grpaddr ) ;
}
if ( precharge )
continue ;
set_jump_as_return ( ) ;
writel ( rwcfg - > zqcl , grpaddr ) ;
/* tZQinit = tDLLK = 512 ck cycles */
delay_for_n_mem_clocks ( 512 ) ;
}
}
/**
* rw_mgr_mem_initialize ( ) - Initialize RW Manager
*
* Initialize RW Manager .
*/
static void rw_mgr_mem_initialize ( void )
{
debug ( " %s:%d \n " , __func__ , __LINE__ ) ;
/* The reset / cke part of initialization is broadcasted to all ranks */
writel ( RW_MGR_RANK_ALL , SDR_PHYGRP_RWMGRGRP_ADDRESS |
RW_MGR_SET_CS_AND_ODT_MASK_OFFSET ) ;
/*
* Here ' s how you load register for a loop
* Counters are located @ 0x800
* Jump address are located @ 0xC00
* For both , registers 0 to 3 are selected using bits 3 and 2 , like
* in 0x800 , 0x804 , 0x808 , 0x80C and 0xC00 , 0xC04 , 0xC08 , 0xC0C
* I know this ain ' t pretty , but Avalon bus throws away the 2 least
* significant bits
*/
/* Start with memory RESET activated */
/* tINIT = 200us */
/*
* 200u s @ 266 MHz ( 3.75 ns ) ~ 54000 clock cycles
* If a and b are the number of iteration in 2 nested loops
* it takes the following number of cycles to complete the operation :
* number_of_cycles = ( ( 2 + n ) * a + 2 ) * b
* where n is the number of instruction in the inner loop
* One possible solution is n = 0 , a = 256 , b = 106 = > a = FF ,
* b = 6 A
*/
rw_mgr_mem_init_load_regs ( misccfg - > tinit_cntr0_val ,
misccfg - > tinit_cntr1_val ,
misccfg - > tinit_cntr2_val ,
rwcfg - > init_reset_0_cke_0 ) ;
/* Indicate that memory is stable. */
writel ( 1 , & phy_mgr_cfg - > reset_mem_stbl ) ;
/*
* transition the RESET to high
* Wait for 500u s
*/
/*
* 500u s @ 266 MHz ( 3.75 ns ) ~ 134000 clock cycles
* If a and b are the number of iteration in 2 nested loops
* it takes the following number of cycles to complete the operation
* number_of_cycles = ( ( 2 + n ) * a + 2 ) * b
* where n is the number of instruction in the inner loop
* One possible solution is n = 2 , a = 131 , b = 256 = > a = 83 ,
* b = FF
*/
rw_mgr_mem_init_load_regs ( misccfg - > treset_cntr0_val ,
misccfg - > treset_cntr1_val ,
misccfg - > treset_cntr2_val ,
rwcfg - > init_reset_1_cke_0 ) ;
/* Bring up clock enable. */
/* tXRP < 250 ck cycles */
delay_for_n_mem_clocks ( 250 ) ;
rw_mgr_mem_load_user ( rwcfg - > mrs0_dll_reset_mirr , rwcfg - > mrs0_dll_reset ,
0 ) ;
}
/**
* rw_mgr_mem_handoff ( ) - Hand off the memory to user
*
* At the end of calibration we have to program the user settings in
* and hand off the memory to the user .
*/
static void rw_mgr_mem_handoff ( void )
{
rw_mgr_mem_load_user ( rwcfg - > mrs0_user_mirr , rwcfg - > mrs0_user , 1 ) ;
/*
* Need to wait tMOD ( 12 CK or 15 ns ) time before issuing other
* commands , but we will have plenty of NIOS cycles before actual
* handoff so its okay .
*/
}
/**
* rw_mgr_mem_calibrate_write_test_issue ( ) - Issue write test command
* @ group : Write Group
* @ use_dm : Use DM
*
* Issue write test command . Two variants are provided , one that just tests
* a write pattern and another that tests datamask functionality .
*/
static void rw_mgr_mem_calibrate_write_test_issue ( u32 group ,
u32 test_dm )
{
const u32 quick_write_mode =
( STATIC_CALIB_STEPS & CALIB_SKIP_WRITES ) & &
misccfg - > enable_super_quick_calibration ;
u32 mcc_instruction ;
u32 rw_wl_nop_cycles ;
/*
* Set counter and jump addresses for the right
* number of NOP cycles .
* The number of supported NOP cycles can range from - 1 to infinity
* Three different cases are handled :
*
* 1. For a number of NOP cycles greater than 0 , the RW Mgr looping
* mechanism will be used to insert the right number of NOPs
*
* 2. For a number of NOP cycles equals to 0 , the micro - instruction
* issuing the write command will jump straight to the
* micro - instruction that turns on DQS ( for DDRx ) , or outputs write
* data ( for RLD ) , skipping
* the NOP micro - instruction all together
*
* 3. A number of NOP cycles equal to - 1 indicates that DQS must be
* turned on in the same micro - instruction that issues the write
* command . Then we need
* to directly jump to the micro - instruction that sends out the data
*
* NOTE : Implementing this mechanism uses 2 RW Mgr jump - counters
* ( 2 and 3 ) . One jump - counter ( 0 ) is used to perform multiple
* write - read operations .
* one counter left to issue this command in " multiple-group " mode
*/
rw_wl_nop_cycles = gbl - > rw_wl_nop_cycles ;
if ( rw_wl_nop_cycles = = - 1 ) {
/*
* CNTR 2 - We want to execute the special write operation that
* turns on DQS right away and then skip directly to the
* instruction that sends out the data . We set the counter to a
* large number so that the jump is always taken .
*/
writel ( 0xFF , & sdr_rw_load_mgr_regs - > load_cntr2 ) ;
/* CNTR 3 - Not used */
if ( test_dm ) {
mcc_instruction = rwcfg - > lfsr_wr_rd_dm_bank_0_wl_1 ;
writel ( rwcfg - > lfsr_wr_rd_dm_bank_0_data ,
& sdr_rw_load_jump_mgr_regs - > load_jump_add2 ) ;
writel ( rwcfg - > lfsr_wr_rd_dm_bank_0_nop ,
& sdr_rw_load_jump_mgr_regs - > load_jump_add3 ) ;
} else {
mcc_instruction = rwcfg - > lfsr_wr_rd_bank_0_wl_1 ;
writel ( rwcfg - > lfsr_wr_rd_bank_0_data ,
& sdr_rw_load_jump_mgr_regs - > load_jump_add2 ) ;
writel ( rwcfg - > lfsr_wr_rd_bank_0_nop ,
& sdr_rw_load_jump_mgr_regs - > load_jump_add3 ) ;
}
} else if ( rw_wl_nop_cycles = = 0 ) {
/*
* CNTR 2 - We want to skip the NOP operation and go straight
* to the DQS enable instruction . We set the counter to a large
* number so that the jump is always taken .
*/
writel ( 0xFF , & sdr_rw_load_mgr_regs - > load_cntr2 ) ;
/* CNTR 3 - Not used */
if ( test_dm ) {
mcc_instruction = rwcfg - > lfsr_wr_rd_dm_bank_0 ;
writel ( rwcfg - > lfsr_wr_rd_dm_bank_0_dqs ,
& sdr_rw_load_jump_mgr_regs - > load_jump_add2 ) ;
} else {
mcc_instruction = rwcfg - > lfsr_wr_rd_bank_0 ;
writel ( rwcfg - > lfsr_wr_rd_bank_0_dqs ,
& sdr_rw_load_jump_mgr_regs - > load_jump_add2 ) ;
}
} else {
/*
* CNTR 2 - In this case we want to execute the next instruction
* and NOT take the jump . So we set the counter to 0. The jump
* address doesn ' t count .
*/
writel ( 0x0 , & sdr_rw_load_mgr_regs - > load_cntr2 ) ;
writel ( 0x0 , & sdr_rw_load_jump_mgr_regs - > load_jump_add2 ) ;
/*
* CNTR 3 - Set the nop counter to the number of cycles we
* need to loop for , minus 1.
*/
writel ( rw_wl_nop_cycles - 1 , & sdr_rw_load_mgr_regs - > load_cntr3 ) ;
if ( test_dm ) {
mcc_instruction = rwcfg - > lfsr_wr_rd_dm_bank_0 ;
writel ( rwcfg - > lfsr_wr_rd_dm_bank_0_nop ,
& sdr_rw_load_jump_mgr_regs - > load_jump_add3 ) ;
} else {
mcc_instruction = rwcfg - > lfsr_wr_rd_bank_0 ;
writel ( rwcfg - > lfsr_wr_rd_bank_0_nop ,
& sdr_rw_load_jump_mgr_regs - > load_jump_add3 ) ;
}
}
writel ( 0 , SDR_PHYGRP_RWMGRGRP_ADDRESS |
RW_MGR_RESET_READ_DATAPATH_OFFSET ) ;
if ( quick_write_mode )
writel ( 0x08 , & sdr_rw_load_mgr_regs - > load_cntr0 ) ;
else
writel ( 0x40 , & sdr_rw_load_mgr_regs - > load_cntr0 ) ;
writel ( mcc_instruction , & sdr_rw_load_jump_mgr_regs - > load_jump_add0 ) ;
/*
* CNTR 1 - This is used to ensure enough time elapses
* for read data to come back .
*/
writel ( 0x30 , & sdr_rw_load_mgr_regs - > load_cntr1 ) ;
if ( test_dm ) {
writel ( rwcfg - > lfsr_wr_rd_dm_bank_0_wait ,
& sdr_rw_load_jump_mgr_regs - > load_jump_add1 ) ;
} else {
writel ( rwcfg - > lfsr_wr_rd_bank_0_wait ,
& sdr_rw_load_jump_mgr_regs - > load_jump_add1 ) ;
}
writel ( mcc_instruction , ( SDR_PHYGRP_RWMGRGRP_ADDRESS |
RW_MGR_RUN_SINGLE_GROUP_OFFSET ) +
( group < < 2 ) ) ;
}
/**
* rw_mgr_mem_calibrate_write_test ( ) - Test writes , check for single / multiple pass
* @ rank_bgn : Rank number
* @ write_group : Write Group
* @ use_dm : Use DM
* @ all_correct : All bits must be correct in the mask
* @ bit_chk : Resulting bit mask after the test
* @ all_ranks : Test all ranks
*
* Test writes , can check for a single bit pass or multiple bit pass .
*/
static int
rw_mgr_mem_calibrate_write_test ( const u32 rank_bgn , const u32 write_group ,
const u32 use_dm , const u32 all_correct ,
u32 * bit_chk , const u32 all_ranks )
{
const u32 rank_end = all_ranks ?
rwcfg - > mem_number_of_ranks :
( rank_bgn + NUM_RANKS_PER_SHADOW_REG ) ;
const u32 shift_ratio = rwcfg - > mem_dq_per_write_dqs /
rwcfg - > mem_virtual_groups_per_write_dqs ;
const u32 correct_mask_vg = param - > write_correct_mask_vg ;
u32 tmp_bit_chk , base_rw_mgr ;
int vg , r ;
* bit_chk = param - > write_correct_mask ;
for ( r = rank_bgn ; r < rank_end ; r + + ) {
/* Set rank */
set_rank_and_odt_mask ( r , RW_MGR_ODT_MODE_READ_WRITE ) ;
tmp_bit_chk = 0 ;
for ( vg = rwcfg - > mem_virtual_groups_per_write_dqs - 1 ;
vg > = 0 ; vg - - ) {
/* Reset the FIFOs to get pointers to known state. */
writel ( 0 , & phy_mgr_cmd - > fifo_reset ) ;
rw_mgr_mem_calibrate_write_test_issue (
write_group *
rwcfg - > mem_virtual_groups_per_write_dqs + vg ,
use_dm ) ;
base_rw_mgr = readl ( SDR_PHYGRP_RWMGRGRP_ADDRESS ) ;
tmp_bit_chk < < = shift_ratio ;
tmp_bit_chk | = ( correct_mask_vg & ~ ( base_rw_mgr ) ) ;
}
* bit_chk & = tmp_bit_chk ;
}
set_rank_and_odt_mask ( 0 , RW_MGR_ODT_MODE_OFF ) ;
if ( all_correct ) {
debug_cond ( DLEVEL = = 2 ,
" write_test(%u,%u,ALL) : %u == %u => %i \n " ,
write_group , use_dm , * bit_chk ,
param - > write_correct_mask ,
* bit_chk = = param - > write_correct_mask ) ;
return * bit_chk = = param - > write_correct_mask ;
} else {
set_rank_and_odt_mask ( 0 , RW_MGR_ODT_MODE_OFF ) ;
debug_cond ( DLEVEL = = 2 ,
" write_test(%u,%u,ONE) : %u != %i => %i \n " ,
write_group , use_dm , * bit_chk , 0 , * bit_chk ! = 0 ) ;
return * bit_chk ! = 0x00 ;
}
}
/**
* rw_mgr_mem_calibrate_read_test_patterns ( ) - Read back test patterns
* @ rank_bgn : Rank number
* @ group : Read / Write Group
* @ all_ranks : Test all ranks
*
* Performs a guaranteed read on the patterns we are going to use during a
* read test to ensure memory works .
*/
static int
rw_mgr_mem_calibrate_read_test_patterns ( const u32 rank_bgn , const u32 group ,
const u32 all_ranks )
{
const u32 addr = SDR_PHYGRP_RWMGRGRP_ADDRESS |
RW_MGR_RUN_SINGLE_GROUP_OFFSET ;
const u32 addr_offset =
( group * rwcfg - > mem_virtual_groups_per_read_dqs ) < < 2 ;
const u32 rank_end = all_ranks ?
rwcfg - > mem_number_of_ranks :
( rank_bgn + NUM_RANKS_PER_SHADOW_REG ) ;
const u32 shift_ratio = rwcfg - > mem_dq_per_read_dqs /
rwcfg - > mem_virtual_groups_per_read_dqs ;
const u32 correct_mask_vg = param - > read_correct_mask_vg ;
u32 tmp_bit_chk , base_rw_mgr , bit_chk ;
int vg , r ;
int ret = 0 ;
bit_chk = param - > read_correct_mask ;
for ( r = rank_bgn ; r < rank_end ; r + + ) {
/* Set rank */
set_rank_and_odt_mask ( r , RW_MGR_ODT_MODE_READ_WRITE ) ;
/* Load up a constant bursts of read commands */
writel ( 0x20 , & sdr_rw_load_mgr_regs - > load_cntr0 ) ;
writel ( rwcfg - > guaranteed_read ,
& sdr_rw_load_jump_mgr_regs - > load_jump_add0 ) ;
writel ( 0x20 , & sdr_rw_load_mgr_regs - > load_cntr1 ) ;
writel ( rwcfg - > guaranteed_read_cont ,
& sdr_rw_load_jump_mgr_regs - > load_jump_add1 ) ;
tmp_bit_chk = 0 ;
for ( vg = rwcfg - > mem_virtual_groups_per_read_dqs - 1 ;
vg > = 0 ; vg - - ) {
/* Reset the FIFOs to get pointers to known state. */
writel ( 0 , & phy_mgr_cmd - > fifo_reset ) ;
writel ( 0 , SDR_PHYGRP_RWMGRGRP_ADDRESS |
RW_MGR_RESET_READ_DATAPATH_OFFSET ) ;
writel ( rwcfg - > guaranteed_read ,
addr + addr_offset + ( vg < < 2 ) ) ;
base_rw_mgr = readl ( SDR_PHYGRP_RWMGRGRP_ADDRESS ) ;
tmp_bit_chk < < = shift_ratio ;
tmp_bit_chk | = correct_mask_vg & ~ base_rw_mgr ;
}
bit_chk & = tmp_bit_chk ;
}
writel ( rwcfg - > clear_dqs_enable , addr + ( group < < 2 ) ) ;
set_rank_and_odt_mask ( 0 , RW_MGR_ODT_MODE_OFF ) ;
if ( bit_chk ! = param - > read_correct_mask )
ret = - EIO ;
debug_cond ( DLEVEL = = 1 ,
" %s:%d test_load_patterns(%u,ALL) => (%u == %u) => %i \n " ,
__func__ , __LINE__ , group , bit_chk ,
param - > read_correct_mask , ret ) ;
return ret ;
}
/**
* rw_mgr_mem_calibrate_read_load_patterns ( ) - Load up the patterns for read test
* @ rank_bgn : Rank number
* @ all_ranks : Test all ranks
*
* Load up the patterns we are going to use during a read test .
*/
static void rw_mgr_mem_calibrate_read_load_patterns ( const u32 rank_bgn ,
const int all_ranks )
{
const u32 rank_end = all_ranks ?
rwcfg - > mem_number_of_ranks :
( rank_bgn + NUM_RANKS_PER_SHADOW_REG ) ;
u32 r ;
debug ( " %s:%d \n " , __func__ , __LINE__ ) ;
for ( r = rank_bgn ; r < rank_end ; r + + ) {
/* set rank */
set_rank_and_odt_mask ( r , RW_MGR_ODT_MODE_READ_WRITE ) ;
/* Load up a constant bursts */
writel ( 0x20 , & sdr_rw_load_mgr_regs - > load_cntr0 ) ;
writel ( rwcfg - > guaranteed_write_wait0 ,
& sdr_rw_load_jump_mgr_regs - > load_jump_add0 ) ;
writel ( 0x20 , & sdr_rw_load_mgr_regs - > load_cntr1 ) ;
writel ( rwcfg - > guaranteed_write_wait1 ,
& sdr_rw_load_jump_mgr_regs - > load_jump_add1 ) ;
writel ( 0x04 , & sdr_rw_load_mgr_regs - > load_cntr2 ) ;
writel ( rwcfg - > guaranteed_write_wait2 ,
& sdr_rw_load_jump_mgr_regs - > load_jump_add2 ) ;
writel ( 0x04 , & sdr_rw_load_mgr_regs - > load_cntr3 ) ;
writel ( rwcfg - > guaranteed_write_wait3 ,
& sdr_rw_load_jump_mgr_regs - > load_jump_add3 ) ;
writel ( rwcfg - > guaranteed_write , SDR_PHYGRP_RWMGRGRP_ADDRESS |
RW_MGR_RUN_SINGLE_GROUP_OFFSET ) ;
}
set_rank_and_odt_mask ( 0 , RW_MGR_ODT_MODE_OFF ) ;
}
/**
* rw_mgr_mem_calibrate_read_test ( ) - Perform READ test on single rank
* @ rank_bgn : Rank number
* @ group : Read / Write group
* @ num_tries : Number of retries of the test
* @ all_correct : All bits must be correct in the mask
* @ bit_chk : Resulting bit mask after the test
* @ all_groups : Test all R / W groups
* @ all_ranks : Test all ranks
*
* Try a read and see if it returns correct data back . Test has dummy reads
* inserted into the mix used to align DQS enable . Test has more thorough
* checks than the regular read test .
*/
static int
rw_mgr_mem_calibrate_read_test ( const u32 rank_bgn , const u32 group ,
const u32 num_tries , const u32 all_correct ,
u32 * bit_chk ,
const u32 all_groups , const u32 all_ranks )
{
const u32 rank_end = all_ranks ? rwcfg - > mem_number_of_ranks :
( rank_bgn + NUM_RANKS_PER_SHADOW_REG ) ;
const u32 quick_read_mode =
( ( STATIC_CALIB_STEPS & CALIB_SKIP_DELAY_SWEEPS ) & &
misccfg - > enable_super_quick_calibration ) ;
u32 correct_mask_vg = param - > read_correct_mask_vg ;
u32 tmp_bit_chk ;
u32 base_rw_mgr ;
u32 addr ;
int r , vg , ret ;
* bit_chk = param - > read_correct_mask ;
for ( r = rank_bgn ; r < rank_end ; r + + ) {
/* set rank */
set_rank_and_odt_mask ( r , RW_MGR_ODT_MODE_READ_WRITE ) ;
writel ( 0x10 , & sdr_rw_load_mgr_regs - > load_cntr1 ) ;
writel ( rwcfg - > read_b2b_wait1 ,
& sdr_rw_load_jump_mgr_regs - > load_jump_add1 ) ;
writel ( 0x10 , & sdr_rw_load_mgr_regs - > load_cntr2 ) ;
writel ( rwcfg - > read_b2b_wait2 ,
& sdr_rw_load_jump_mgr_regs - > load_jump_add2 ) ;
if ( quick_read_mode )
writel ( 0x1 , & sdr_rw_load_mgr_regs - > load_cntr0 ) ;
/* need at least two (1+1) reads to capture failures */
else if ( all_groups )
writel ( 0x06 , & sdr_rw_load_mgr_regs - > load_cntr0 ) ;
else
writel ( 0x32 , & sdr_rw_load_mgr_regs - > load_cntr0 ) ;
writel ( rwcfg - > read_b2b ,
& sdr_rw_load_jump_mgr_regs - > load_jump_add0 ) ;
if ( all_groups )
writel ( rwcfg - > mem_if_read_dqs_width *
rwcfg - > mem_virtual_groups_per_read_dqs - 1 ,
& sdr_rw_load_mgr_regs - > load_cntr3 ) ;
else
writel ( 0x0 , & sdr_rw_load_mgr_regs - > load_cntr3 ) ;
writel ( rwcfg - > read_b2b ,
& sdr_rw_load_jump_mgr_regs - > load_jump_add3 ) ;
tmp_bit_chk = 0 ;
for ( vg = rwcfg - > mem_virtual_groups_per_read_dqs - 1 ; vg > = 0 ;
vg - - ) {
/* Reset the FIFOs to get pointers to known state. */
writel ( 0 , & phy_mgr_cmd - > fifo_reset ) ;
writel ( 0 , SDR_PHYGRP_RWMGRGRP_ADDRESS |
RW_MGR_RESET_READ_DATAPATH_OFFSET ) ;
if ( all_groups ) {
addr = SDR_PHYGRP_RWMGRGRP_ADDRESS |
RW_MGR_RUN_ALL_GROUPS_OFFSET ;
} else {
addr = SDR_PHYGRP_RWMGRGRP_ADDRESS |
RW_MGR_RUN_SINGLE_GROUP_OFFSET ;
}
writel ( rwcfg - > read_b2b , addr +
( ( group *
rwcfg - > mem_virtual_groups_per_read_dqs +
vg ) < < 2 ) ) ;
base_rw_mgr = readl ( SDR_PHYGRP_RWMGRGRP_ADDRESS ) ;
tmp_bit_chk < < = rwcfg - > mem_dq_per_read_dqs /
rwcfg - > mem_virtual_groups_per_read_dqs ;
tmp_bit_chk | = correct_mask_vg & ~ ( base_rw_mgr ) ;
}
* bit_chk & = tmp_bit_chk ;
}
addr = SDR_PHYGRP_RWMGRGRP_ADDRESS | RW_MGR_RUN_SINGLE_GROUP_OFFSET ;
writel ( rwcfg - > clear_dqs_enable , addr + ( group < < 2 ) ) ;
set_rank_and_odt_mask ( 0 , RW_MGR_ODT_MODE_OFF ) ;
if ( all_correct ) {
ret = ( * bit_chk = = param - > read_correct_mask ) ;
debug_cond ( DLEVEL = = 2 ,
" %s:%d read_test(%u,ALL,%u) => (%u == %u) => %i \n " ,
__func__ , __LINE__ , group , all_groups , * bit_chk ,
param - > read_correct_mask , ret ) ;
} else {
ret = ( * bit_chk ! = 0x00 ) ;
debug_cond ( DLEVEL = = 2 ,
" %s:%d read_test(%u,ONE,%u) => (%u != %u) => %i \n " ,
__func__ , __LINE__ , group , all_groups , * bit_chk ,
0 , ret ) ;
}
return ret ;
}
/**
* rw_mgr_mem_calibrate_read_test_all_ranks ( ) - Perform READ test on all ranks
* @ grp : Read / Write group
* @ num_tries : Number of retries of the test
* @ all_correct : All bits must be correct in the mask
* @ all_groups : Test all R / W groups
*
* Perform a READ test across all memory ranks .
*/
static int
rw_mgr_mem_calibrate_read_test_all_ranks ( const u32 grp , const u32 num_tries ,
const u32 all_correct ,
const u32 all_groups )
{
u32 bit_chk ;
return rw_mgr_mem_calibrate_read_test ( 0 , grp , num_tries , all_correct ,
& bit_chk , all_groups , 1 ) ;
}
/**
* rw_mgr_incr_vfifo ( ) - Increase VFIFO value
* @ grp : Read / Write group
*
* Increase VFIFO value .
*/
static void rw_mgr_incr_vfifo ( const u32 grp )
{
writel ( grp , & phy_mgr_cmd - > inc_vfifo_hard_phy ) ;
}
/**
* rw_mgr_decr_vfifo ( ) - Decrease VFIFO value
* @ grp : Read / Write group
*
* Decrease VFIFO value .
*/
static void rw_mgr_decr_vfifo ( const u32 grp )
{
u32 i ;
for ( i = 0 ; i < misccfg - > read_valid_fifo_size - 1 ; i + + )
rw_mgr_incr_vfifo ( grp ) ;
}
/**
* find_vfifo_failing_read ( ) - Push VFIFO to get a failing read
* @ grp : Read / Write group
*
* Push VFIFO until a failing read happens .
*/
static int find_vfifo_failing_read ( const u32 grp )
{
u32 v , ret , fail_cnt = 0 ;
for ( v = 0 ; v < misccfg - > read_valid_fifo_size ; v + + ) {
debug_cond ( DLEVEL = = 2 , " %s:%d: vfifo %u \n " ,
__func__ , __LINE__ , v ) ;
ret = rw_mgr_mem_calibrate_read_test_all_ranks ( grp , 1 ,
PASS_ONE_BIT , 0 ) ;
if ( ! ret ) {
fail_cnt + + ;
if ( fail_cnt = = 2 )
return v ;
}
/* Fiddle with FIFO. */
rw_mgr_incr_vfifo ( grp ) ;
}
/* No failing read found! Something must have gone wrong. */
debug_cond ( DLEVEL = = 2 , " %s:%d: vfifo failed \n " , __func__ , __LINE__ ) ;
return 0 ;
}
/**
* sdr_find_phase_delay ( ) - Find DQS enable phase or delay
* @ working : If 1 , look for working phase / delay , if 0 , look for non - working
* @ delay : If 1 , look for delay , if 0 , look for phase
* @ grp : Read / Write group
* @ work : Working window position
* @ work_inc : Working window increment
* @ pd : DQS Phase / Delay Iterator
*
* Find working or non - working DQS enable phase setting .
*/
static int sdr_find_phase_delay ( int working , int delay , const u32 grp ,
u32 * work , const u32 work_inc , u32 * pd )
{
const u32 max = delay ? iocfg - > dqs_en_delay_max :
iocfg - > dqs_en_phase_max ;
u32 ret ;
for ( ; * pd < = max ; ( * pd ) + + ) {
if ( delay )
scc_mgr_set_dqs_en_delay_all_ranks ( grp , * pd ) ;
else
scc_mgr_set_dqs_en_phase_all_ranks ( grp , * pd ) ;
ret = rw_mgr_mem_calibrate_read_test_all_ranks ( grp , 1 ,
PASS_ONE_BIT , 0 ) ;
if ( ! working )
ret = ! ret ;
if ( ret )
return 0 ;
if ( work )
* work + = work_inc ;
}
return - EINVAL ;
}
/**
* sdr_find_phase ( ) - Find DQS enable phase
* @ working : If 1 , look for working phase , if 0 , look for non - working phase
* @ grp : Read / Write group
* @ work : Working window position
* @ i : Iterator
* @ p : DQS Phase Iterator
*
* Find working or non - working DQS enable phase setting .
*/
static int sdr_find_phase ( int working , const u32 grp , u32 * work ,
u32 * i , u32 * p )
{
const u32 end = misccfg - > read_valid_fifo_size + ( working ? 0 : 1 ) ;
int ret ;
for ( ; * i < end ; ( * i ) + + ) {
if ( working )
* p = 0 ;
ret = sdr_find_phase_delay ( working , 0 , grp , work ,
iocfg - > delay_per_opa_tap , p ) ;
if ( ! ret )
return 0 ;
if ( * p > iocfg - > dqs_en_phase_max ) {
/* Fiddle with FIFO. */
rw_mgr_incr_vfifo ( grp ) ;
if ( ! working )
* p = 0 ;
}
}
return - EINVAL ;
}
/**
* sdr_working_phase ( ) - Find working DQS enable phase
* @ grp : Read / Write group
* @ work_bgn : Working window start position
* @ d : dtaps output value
* @ p : DQS Phase Iterator
* @ i : Iterator
*
* Find working DQS enable phase setting .
*/
static int sdr_working_phase ( const u32 grp , u32 * work_bgn , u32 * d ,
u32 * p , u32 * i )
{
const u32 dtaps_per_ptap = iocfg - > delay_per_opa_tap /
iocfg - > delay_per_dqs_en_dchain_tap ;
int ret ;
* work_bgn = 0 ;
for ( * d = 0 ; * d < = dtaps_per_ptap ; ( * d ) + + ) {
* i = 0 ;
scc_mgr_set_dqs_en_delay_all_ranks ( grp , * d ) ;
ret = sdr_find_phase ( 1 , grp , work_bgn , i , p ) ;
if ( ! ret )
return 0 ;
* work_bgn + = iocfg - > delay_per_dqs_en_dchain_tap ;
}
/* Cannot find working solution */
debug_cond ( DLEVEL = = 2 , " %s:%d find_dqs_en_phase: no vfifo/ptap/dtap \n " ,
__func__ , __LINE__ ) ;
return - EINVAL ;
}
/**
* sdr_backup_phase ( ) - Find DQS enable backup phase
* @ grp : Read / Write group
* @ work_bgn : Working window start position
* @ p : DQS Phase Iterator
*
* Find DQS enable backup phase setting .
*/
static void sdr_backup_phase ( const u32 grp , u32 * work_bgn , u32 * p )
{
u32 tmp_delay , d ;
int ret ;
/* Special case code for backing up a phase */
if ( * p = = 0 ) {
* p = iocfg - > dqs_en_phase_max ;
rw_mgr_decr_vfifo ( grp ) ;
} else {
( * p ) - - ;
}
tmp_delay = * work_bgn - iocfg - > delay_per_opa_tap ;
scc_mgr_set_dqs_en_phase_all_ranks ( grp , * p ) ;
for ( d = 0 ; d < = iocfg - > dqs_en_delay_max & & tmp_delay < * work_bgn ;
d + + ) {
scc_mgr_set_dqs_en_delay_all_ranks ( grp , d ) ;
ret = rw_mgr_mem_calibrate_read_test_all_ranks ( grp , 1 ,
PASS_ONE_BIT , 0 ) ;
if ( ret ) {
* work_bgn = tmp_delay ;
break ;
}
tmp_delay + = iocfg - > delay_per_dqs_en_dchain_tap ;
}
/* Restore VFIFO to old state before we decremented it (if needed). */
( * p ) + + ;
if ( * p > iocfg - > dqs_en_phase_max ) {
* p = 0 ;
rw_mgr_incr_vfifo ( grp ) ;
}
scc_mgr_set_dqs_en_delay_all_ranks ( grp , 0 ) ;
}
/**
* sdr_nonworking_phase ( ) - Find non - working DQS enable phase
* @ grp : Read / Write group
* @ work_end : Working window end position
* @ p : DQS Phase Iterator
* @ i : Iterator
*
* Find non - working DQS enable phase setting .
*/
static int sdr_nonworking_phase ( const u32 grp , u32 * work_end , u32 * p , u32 * i )
{
int ret ;
( * p ) + + ;
* work_end + = iocfg - > delay_per_opa_tap ;
if ( * p > iocfg - > dqs_en_phase_max ) {
/* Fiddle with FIFO. */
* p = 0 ;
rw_mgr_incr_vfifo ( grp ) ;
}
ret = sdr_find_phase ( 0 , grp , work_end , i , p ) ;
if ( ret ) {
/* Cannot see edge of failing read. */
debug_cond ( DLEVEL = = 2 , " %s:%d: end: failed \n " ,
__func__ , __LINE__ ) ;
}
return ret ;
}
/**
* sdr_find_window_center ( ) - Find center of the working DQS window .
* @ grp : Read / Write group
* @ work_bgn : First working settings
* @ work_end : Last working settings
*
* Find center of the working DQS enable window .
*/
static int sdr_find_window_center ( const u32 grp , const u32 work_bgn ,
const u32 work_end )
{
u32 work_mid ;
int tmp_delay = 0 ;
int i , p , d ;
work_mid = ( work_bgn + work_end ) / 2 ;
debug_cond ( DLEVEL = = 2 , " work_bgn=%d work_end=%d work_mid=%d \n " ,
work_bgn , work_end , work_mid ) ;
/* Get the middle delay to be less than a VFIFO delay */
tmp_delay = ( iocfg - > dqs_en_phase_max + 1 ) * iocfg - > delay_per_opa_tap ;
debug_cond ( DLEVEL = = 2 , " vfifo ptap delay %d \n " , tmp_delay ) ;
work_mid % = tmp_delay ;
debug_cond ( DLEVEL = = 2 , " new work_mid %d \n " , work_mid ) ;
tmp_delay = rounddown ( work_mid , iocfg - > delay_per_opa_tap ) ;
if ( tmp_delay > iocfg - > dqs_en_phase_max * iocfg - > delay_per_opa_tap )
tmp_delay = iocfg - > dqs_en_phase_max * iocfg - > delay_per_opa_tap ;
p = tmp_delay / iocfg - > delay_per_opa_tap ;
debug_cond ( DLEVEL = = 2 , " new p %d, tmp_delay=%d \n " , p , tmp_delay ) ;
d = DIV_ROUND_UP ( work_mid - tmp_delay ,
iocfg - > delay_per_dqs_en_dchain_tap ) ;
if ( d > iocfg - > dqs_en_delay_max )
d = iocfg - > dqs_en_delay_max ;
tmp_delay + = d * iocfg - > delay_per_dqs_en_dchain_tap ;
debug_cond ( DLEVEL = = 2 , " new d %d, tmp_delay=%d \n " , d , tmp_delay ) ;
scc_mgr_set_dqs_en_phase_all_ranks ( grp , p ) ;
scc_mgr_set_dqs_en_delay_all_ranks ( grp , d ) ;
/*
* push vfifo until we can successfully calibrate . We can do this
* because the largest possible margin in 1 VFIFO cycle .
*/
for ( i = 0 ; i < misccfg - > read_valid_fifo_size ; i + + ) {
debug_cond ( DLEVEL = = 2 , " find_dqs_en_phase: center \n " ) ;
if ( rw_mgr_mem_calibrate_read_test_all_ranks ( grp , 1 ,
PASS_ONE_BIT ,
0 ) ) {
debug_cond ( DLEVEL = = 2 ,
" %s:%d center: found: ptap=%u dtap=%u \n " ,
__func__ , __LINE__ , p , d ) ;
return 0 ;
}
/* Fiddle with FIFO. */
rw_mgr_incr_vfifo ( grp ) ;
}
debug_cond ( DLEVEL = = 2 , " %s:%d center: failed. \n " ,
__func__ , __LINE__ ) ;
return - EINVAL ;
}
/**
* rw_mgr_mem_calibrate_vfifo_find_dqs_en_phase ( ) - Find a good DQS enable to use
* @ grp : Read / Write Group
*
* Find a good DQS enable to use .
*/
static int rw_mgr_mem_calibrate_vfifo_find_dqs_en_phase ( const u32 grp )
{
u32 d , p , i ;
u32 dtaps_per_ptap ;
u32 work_bgn , work_end ;
u32 found_passing_read , found_failing_read = 0 , initial_failing_dtap ;
int ret ;
debug ( " %s:%d %u \n " , __func__ , __LINE__ , grp ) ;
reg_file_set_sub_stage ( CAL_SUBSTAGE_VFIFO_CENTER ) ;
scc_mgr_set_dqs_en_delay_all_ranks ( grp , 0 ) ;
scc_mgr_set_dqs_en_phase_all_ranks ( grp , 0 ) ;
/* Step 0: Determine number of delay taps for each phase tap. */
dtaps_per_ptap = iocfg - > delay_per_opa_tap /
iocfg - > delay_per_dqs_en_dchain_tap ;
/* Step 1: First push vfifo until we get a failing read. */
find_vfifo_failing_read ( grp ) ;
/* Step 2: Find first working phase, increment in ptaps. */
work_bgn = 0 ;
ret = sdr_working_phase ( grp , & work_bgn , & d , & p , & i ) ;
if ( ret )
return ret ;
work_end = work_bgn ;
/*
* If d is 0 then the working window covers a phase tap and we can
* follow the old procedure . Otherwise , we ' ve found the beginning
* and we need to increment the dtaps until we find the end .
*/
if ( d = = 0 ) {
/*
* Step 3 a : If we have room , back off by one and
* increment in dtaps .
*/
sdr_backup_phase ( grp , & work_bgn , & p ) ;
/*
* Step 4 a : go forward from working phase to non working
* phase , increment in ptaps .
*/
ret = sdr_nonworking_phase ( grp , & work_end , & p , & i ) ;
if ( ret )
return ret ;
/* Step 5a: Back off one from last, increment in dtaps. */
/* Special case code for backing up a phase */
if ( p = = 0 ) {
p = iocfg - > dqs_en_phase_max ;
rw_mgr_decr_vfifo ( grp ) ;
} else {
p = p - 1 ;
}
work_end - = iocfg - > delay_per_opa_tap ;
scc_mgr_set_dqs_en_phase_all_ranks ( grp , p ) ;
d = 0 ;
debug_cond ( DLEVEL = = 2 , " %s:%d p: ptap=%u \n " ,
__func__ , __LINE__ , p ) ;
}
/* The dtap increment to find the failing edge is done here. */
sdr_find_phase_delay ( 0 , 1 , grp , & work_end ,
iocfg - > delay_per_dqs_en_dchain_tap , & d ) ;
/* Go back to working dtap */
if ( d ! = 0 )
work_end - = iocfg - > delay_per_dqs_en_dchain_tap ;
debug_cond ( DLEVEL = = 2 ,
" %s:%d p/d: ptap=%u dtap=%u end=%u \n " ,
__func__ , __LINE__ , p , d - 1 , work_end ) ;
if ( work_end < work_bgn ) {
/* nil range */
debug_cond ( DLEVEL = = 2 , " %s:%d end-2: failed \n " ,
__func__ , __LINE__ ) ;
return - EINVAL ;
}
debug_cond ( DLEVEL = = 2 , " %s:%d found range [%u,%u] \n " ,
__func__ , __LINE__ , work_bgn , work_end ) ;
/*
* We need to calculate the number of dtaps that equal a ptap .
* To do that we ' ll back up a ptap and re - find the edge of the
* window using dtaps
*/
debug_cond ( DLEVEL = = 2 , " %s:%d calculate dtaps_per_ptap for tracking \n " ,
__func__ , __LINE__ ) ;
/* Special case code for backing up a phase */
if ( p = = 0 ) {
p = iocfg - > dqs_en_phase_max ;
rw_mgr_decr_vfifo ( grp ) ;
debug_cond ( DLEVEL = = 2 , " %s:%d backedup cycle/phase: p=%u \n " ,
__func__ , __LINE__ , p ) ;
} else {
p = p - 1 ;
debug_cond ( DLEVEL = = 2 , " %s:%d backedup phase only: p=%u " ,
__func__ , __LINE__ , p ) ;
}
scc_mgr_set_dqs_en_phase_all_ranks ( grp , p ) ;
/*
* Increase dtap until we first see a passing read ( in case the
* window is smaller than a ptap ) , and then a failing read to
* mark the edge of the window again .
*/
/* Find a passing read. */
debug_cond ( DLEVEL = = 2 , " %s:%d find passing read \n " ,
__func__ , __LINE__ ) ;
initial_failing_dtap = d ;
found_passing_read = ! sdr_find_phase_delay ( 1 , 1 , grp , NULL , 0 , & d ) ;
if ( found_passing_read ) {
/* Find a failing read. */
debug_cond ( DLEVEL = = 2 , " %s:%d find failing read \n " ,
__func__ , __LINE__ ) ;
d + + ;
found_failing_read = ! sdr_find_phase_delay ( 0 , 1 , grp , NULL , 0 ,
& d ) ;
} else {
debug_cond ( DLEVEL = = 1 ,
" %s:%d failed to calculate dtaps per ptap. Fall back on static value \n " ,
__func__ , __LINE__ ) ;
}
/*
* The dynamically calculated dtaps_per_ptap is only valid if we
* found a passing / failing read . If we didn ' t , it means d hit the max
* ( iocfg - > dqs_en_delay_max ) . Otherwise , dtaps_per_ptap retains its
* statically calculated value .
*/
if ( found_passing_read & & found_failing_read )
dtaps_per_ptap = d - initial_failing_dtap ;
writel ( dtaps_per_ptap , & sdr_reg_file - > dtaps_per_ptap ) ;
debug_cond ( DLEVEL = = 2 , " %s:%d dtaps_per_ptap=%u - %u = %u " ,
__func__ , __LINE__ , d , initial_failing_dtap , dtaps_per_ptap ) ;
/* Step 6: Find the centre of the window. */
ret = sdr_find_window_center ( grp , work_bgn , work_end ) ;
return ret ;
}
/**
* search_stop_check ( ) - Check if the detected edge is valid
* @ write : Perform read ( Stage 2 ) or write ( Stage 3 ) calibration
* @ d : DQS delay
* @ rank_bgn : Rank number
* @ write_group : Write Group
* @ read_group : Read Group
* @ bit_chk : Resulting bit mask after the test
* @ sticky_bit_chk : Resulting sticky bit mask after the test
* @ use_read_test : Perform read test
*
* Test if the found edge is valid .
*/
static u32 search_stop_check ( const int write , const int d , const int rank_bgn ,
const u32 write_group , const u32 read_group ,
u32 * bit_chk , u32 * sticky_bit_chk ,
const u32 use_read_test )
{
const u32 ratio = rwcfg - > mem_if_read_dqs_width /
rwcfg - > mem_if_write_dqs_width ;
const u32 correct_mask = write ? param - > write_correct_mask :
param - > read_correct_mask ;
const u32 per_dqs = write ? rwcfg - > mem_dq_per_write_dqs :
rwcfg - > mem_dq_per_read_dqs ;
u32 ret ;
/*
* Stop searching when the read test doesn ' t pass AND when
* we ' ve seen a passing read on every bit .
*/
if ( write ) { /* WRITE-ONLY */
ret = ! rw_mgr_mem_calibrate_write_test ( rank_bgn , write_group ,
0 , PASS_ONE_BIT ,
bit_chk , 0 ) ;
} else if ( use_read_test ) { /* READ-ONLY */
ret = ! rw_mgr_mem_calibrate_read_test ( rank_bgn , read_group ,
NUM_READ_PB_TESTS ,
PASS_ONE_BIT , bit_chk ,
0 , 0 ) ;
} else { /* READ-ONLY */
rw_mgr_mem_calibrate_write_test ( rank_bgn , write_group , 0 ,
PASS_ONE_BIT , bit_chk , 0 ) ;
* bit_chk = * bit_chk > > ( per_dqs *
( read_group - ( write_group * ratio ) ) ) ;
ret = ( * bit_chk = = 0 ) ;
}
* sticky_bit_chk = * sticky_bit_chk | * bit_chk ;
ret = ret & & ( * sticky_bit_chk = = correct_mask ) ;
debug_cond ( DLEVEL = = 2 ,
" %s:%d center(left): dtap=%u => %u == %u && %u " ,
__func__ , __LINE__ , d ,
* sticky_bit_chk , correct_mask , ret ) ;
return ret ;
}
/**
* search_left_edge ( ) - Find left edge of DQ / DQS working phase
* @ write : Perform read ( Stage 2 ) or write ( Stage 3 ) calibration
* @ rank_bgn : Rank number
* @ write_group : Write Group
* @ read_group : Read Group
* @ test_bgn : Rank number to begin the test
* @ sticky_bit_chk : Resulting sticky bit mask after the test
* @ left_edge : Left edge of the DQ / DQS phase
* @ right_edge : Right edge of the DQ / DQS phase
* @ use_read_test : Perform read test
*
* Find left edge of DQ / DQS working phase .
*/
static void search_left_edge ( const int write , const int rank_bgn ,
const u32 write_group , const u32 read_group , const u32 test_bgn ,
u32 * sticky_bit_chk ,
int * left_edge , int * right_edge , const u32 use_read_test )
{
const u32 delay_max = write ? iocfg - > io_out1_delay_max :
iocfg - > io_in_delay_max ;
const u32 dqs_max = write ? iocfg - > io_out1_delay_max :
iocfg - > dqs_in_delay_max ;
const u32 per_dqs = write ? rwcfg - > mem_dq_per_write_dqs :
rwcfg - > mem_dq_per_read_dqs ;
u32 stop , bit_chk ;
int i , d ;
for ( d = 0 ; d < = dqs_max ; d + + ) {
if ( write )
scc_mgr_apply_group_dq_out1_delay ( d ) ;
else
scc_mgr_apply_group_dq_in_delay ( test_bgn , d ) ;
writel ( 0 , & sdr_scc_mgr - > update ) ;
stop = search_stop_check ( write , d , rank_bgn , write_group ,
read_group , & bit_chk , sticky_bit_chk ,
use_read_test ) ;
if ( stop = = 1 )
break ;
/* stop != 1 */
for ( i = 0 ; i < per_dqs ; i + + ) {
if ( bit_chk & 1 ) {
/*
* Remember a passing test as
* the left_edge .
*/
left_edge [ i ] = d ;
} else {
/*
* If a left edge has not been seen
* yet , then a future passing test
* will mark this edge as the right
* edge .
*/
if ( left_edge [ i ] = = delay_max + 1 )
right_edge [ i ] = - ( d + 1 ) ;
}
bit_chk > > = 1 ;
}
}
/* Reset DQ delay chains to 0 */
if ( write )
scc_mgr_apply_group_dq_out1_delay ( 0 ) ;
else
scc_mgr_apply_group_dq_in_delay ( test_bgn , 0 ) ;
* sticky_bit_chk = 0 ;
for ( i = per_dqs - 1 ; i > = 0 ; i - - ) {
debug_cond ( DLEVEL = = 2 ,
" %s:%d vfifo_center: left_edge[%u]: %d right_edge[%u]: %d \n " ,
__func__ , __LINE__ , i , left_edge [ i ] ,
i , right_edge [ i ] ) ;
/*
* Check for cases where we haven ' t found the left edge ,
* which makes our assignment of the the right edge invalid .
* Reset it to the illegal value .
*/
if ( ( left_edge [ i ] = = delay_max + 1 ) & &
( right_edge [ i ] ! = delay_max + 1 ) ) {
right_edge [ i ] = delay_max + 1 ;
debug_cond ( DLEVEL = = 2 ,
" %s:%d vfifo_center: reset right_edge[%u]: %d \n " ,
__func__ , __LINE__ , i , right_edge [ i ] ) ;
}
/*
* Reset sticky bit
* READ : except for bits where we have seen both
* the left and right edge .
* WRITE : except for bits where we have seen the
* left edge .
*/
* sticky_bit_chk < < = 1 ;
if ( write ) {
if ( left_edge [ i ] ! = delay_max + 1 )
* sticky_bit_chk | = 1 ;
} else {
if ( ( left_edge [ i ] ! = delay_max + 1 ) & &
( right_edge [ i ] ! = delay_max + 1 ) )
* sticky_bit_chk | = 1 ;
}
}
}
/**
* search_right_edge ( ) - Find right edge of DQ / DQS working phase
* @ write : Perform read ( Stage 2 ) or write ( Stage 3 ) calibration
* @ rank_bgn : Rank number
* @ write_group : Write Group
* @ read_group : Read Group
* @ start_dqs : DQS start phase
* @ start_dqs_en : DQS enable start phase
* @ sticky_bit_chk : Resulting sticky bit mask after the test
* @ left_edge : Left edge of the DQ / DQS phase
* @ right_edge : Right edge of the DQ / DQS phase
* @ use_read_test : Perform read test
*
* Find right edge of DQ / DQS working phase .
*/
static int search_right_edge ( const int write , const int rank_bgn ,
const u32 write_group , const u32 read_group ,
const int start_dqs , const int start_dqs_en ,
u32 * sticky_bit_chk ,
int * left_edge , int * right_edge , const u32 use_read_test )
{
const u32 delay_max = write ? iocfg - > io_out1_delay_max :
iocfg - > io_in_delay_max ;
const u32 dqs_max = write ? iocfg - > io_out1_delay_max :
iocfg - > dqs_in_delay_max ;
const u32 per_dqs = write ? rwcfg - > mem_dq_per_write_dqs :
rwcfg - > mem_dq_per_read_dqs ;
u32 stop , bit_chk ;
int i , d ;
for ( d = 0 ; d < = dqs_max - start_dqs ; d + + ) {
if ( write ) { /* WRITE-ONLY */
scc_mgr_apply_group_dqs_io_and_oct_out1 ( write_group ,
d + start_dqs ) ;
} else { /* READ-ONLY */
scc_mgr_set_dqs_bus_in_delay ( read_group , d + start_dqs ) ;
if ( iocfg - > shift_dqs_en_when_shift_dqs ) {
u32 delay = d + start_dqs_en ;
if ( delay > iocfg - > dqs_en_delay_max )
delay = iocfg - > dqs_en_delay_max ;
scc_mgr_set_dqs_en_delay ( read_group , delay ) ;
}
scc_mgr_load_dqs ( read_group ) ;
}
writel ( 0 , & sdr_scc_mgr - > update ) ;
stop = search_stop_check ( write , d , rank_bgn , write_group ,
read_group , & bit_chk , sticky_bit_chk ,
use_read_test ) ;
if ( stop = = 1 ) {
if ( write & & ( d = = 0 ) ) { /* WRITE-ONLY */
for ( i = 0 ; i < rwcfg - > mem_dq_per_write_dqs ;
i + + ) {
/*
* d = 0 failed , but it passed when
* testing the left edge , so it must be
* marginal , set it to - 1
*/
if ( right_edge [ i ] = = delay_max + 1 & &
left_edge [ i ] ! = delay_max + 1 )
right_edge [ i ] = - 1 ;
}
}
break ;
}
/* stop != 1 */
for ( i = 0 ; i < per_dqs ; i + + ) {
if ( bit_chk & 1 ) {
/*
* Remember a passing test as
* the right_edge .
*/
right_edge [ i ] = d ;
} else {
if ( d ! = 0 ) {
/*
* If a right edge has not
* been seen yet , then a future
* passing test will mark this
* edge as the left edge .
*/
if ( right_edge [ i ] = = delay_max + 1 )
left_edge [ i ] = - ( d + 1 ) ;
} else {
/*
* d = 0 failed , but it passed
* when testing the left edge ,
* so it must be marginal , set
* it to - 1
*/
if ( right_edge [ i ] = = delay_max + 1 & &
left_edge [ i ] ! = delay_max + 1 )
right_edge [ i ] = - 1 ;
/*
* If a right edge has not been
* seen yet , then a future
* passing test will mark this
* edge as the left edge .
*/
else if ( right_edge [ i ] = = delay_max + 1 )
left_edge [ i ] = - ( d + 1 ) ;
}
}
debug_cond ( DLEVEL = = 2 , " %s:%d center[r,d=%u]: " ,
__func__ , __LINE__ , d ) ;
debug_cond ( DLEVEL = = 2 ,
" bit_chk_test=%i left_edge[%u]: %d " ,
bit_chk & 1 , i , left_edge [ i ] ) ;
debug_cond ( DLEVEL = = 2 , " right_edge[%u]: %d \n " , i ,
right_edge [ i ] ) ;
bit_chk > > = 1 ;
}
}
/* Check that all bits have a window */
for ( i = 0 ; i < per_dqs ; i + + ) {
debug_cond ( DLEVEL = = 2 ,
" %s:%d write_center: left_edge[%u]: %d right_edge[%u]: %d " ,
__func__ , __LINE__ , i , left_edge [ i ] ,
i , right_edge [ i ] ) ;
if ( ( left_edge [ i ] = = dqs_max + 1 ) | |
( right_edge [ i ] = = dqs_max + 1 ) )
return i + 1 ; /* FIXME: If we fail, retval > 0 */
}
return 0 ;
}
/**
* get_window_mid_index ( ) - Find the best middle setting of DQ / DQS phase
* @ write : Perform read ( Stage 2 ) or write ( Stage 3 ) calibration
* @ left_edge : Left edge of the DQ / DQS phase
* @ right_edge : Right edge of the DQ / DQS phase
* @ mid_min : Best DQ / DQS phase middle setting
*
* Find index and value of the middle of the DQ / DQS working phase .
*/
static int get_window_mid_index ( const int write , int * left_edge ,
int * right_edge , int * mid_min )
{
const u32 per_dqs = write ? rwcfg - > mem_dq_per_write_dqs :
rwcfg - > mem_dq_per_read_dqs ;
int i , mid , min_index ;
/* Find middle of window for each DQ bit */
* mid_min = left_edge [ 0 ] - right_edge [ 0 ] ;
min_index = 0 ;
for ( i = 1 ; i < per_dqs ; i + + ) {
mid = left_edge [ i ] - right_edge [ i ] ;
if ( mid < * mid_min ) {
* mid_min = mid ;
min_index = i ;
}
}
/*
* - mid_min / 2 represents the amount that we need to move DQS .
* If mid_min is odd and positive we ' ll need to add one to make
* sure the rounding in further calculations is correct ( always
* bias to the right ) , so just add 1 for all positive values .
*/
if ( * mid_min > 0 )
( * mid_min ) + + ;
* mid_min = * mid_min / 2 ;
debug_cond ( DLEVEL = = 1 , " %s:%d vfifo_center: *mid_min=%d (index=%u) \n " ,
__func__ , __LINE__ , * mid_min , min_index ) ;
return min_index ;
}
/**
* center_dq_windows ( ) - Center the DQ / DQS windows
* @ write : Perform read ( Stage 2 ) or write ( Stage 3 ) calibration
* @ left_edge : Left edge of the DQ / DQS phase
* @ right_edge : Right edge of the DQ / DQS phase
* @ mid_min : Adjusted DQ / DQS phase middle setting
* @ orig_mid_min : Original DQ / DQS phase middle setting
* @ min_index : DQ / DQS phase middle setting index
* @ test_bgn : Rank number to begin the test
* @ dq_margin : Amount of shift for the DQ
* @ dqs_margin : Amount of shift for the DQS
*
* Align the DQ / DQS windows in each group .
*/
static void center_dq_windows ( const int write , int * left_edge , int * right_edge ,
const int mid_min , const int orig_mid_min ,
const int min_index , const int test_bgn ,
int * dq_margin , int * dqs_margin )
{
const u32 delay_max = write ? iocfg - > io_out1_delay_max :
iocfg - > io_in_delay_max ;
const u32 per_dqs = write ? rwcfg - > mem_dq_per_write_dqs :
rwcfg - > mem_dq_per_read_dqs ;
const u32 delay_off = write ? SCC_MGR_IO_OUT1_DELAY_OFFSET :
SCC_MGR_IO_IN_DELAY_OFFSET ;
const u32 addr = SDR_PHYGRP_SCCGRP_ADDRESS | delay_off ;
u32 temp_dq_io_delay1 , temp_dq_io_delay2 ;
int shift_dq , i , p ;
/* Initialize data for export structures */
* dqs_margin = delay_max + 1 ;
* dq_margin = delay_max + 1 ;
/* add delay to bring centre of all DQ windows to the same "level" */
for ( i = 0 , p = test_bgn ; i < per_dqs ; i + + , p + + ) {
/* Use values before divide by 2 to reduce round off error */
shift_dq = ( left_edge [ i ] - right_edge [ i ] -
( left_edge [ min_index ] - right_edge [ min_index ] ) ) / 2 +
( orig_mid_min - mid_min ) ;
debug_cond ( DLEVEL = = 2 ,
" vfifo_center: before: shift_dq[%u]=%d \n " ,
i , shift_dq ) ;
temp_dq_io_delay1 = readl ( addr + ( p < < 2 ) ) ;
temp_dq_io_delay2 = readl ( addr + ( i < < 2 ) ) ;
if ( shift_dq + temp_dq_io_delay1 > delay_max )
shift_dq = delay_max - temp_dq_io_delay2 ;
else if ( shift_dq + temp_dq_io_delay1 < 0 )
shift_dq = - temp_dq_io_delay1 ;
debug_cond ( DLEVEL = = 2 ,
" vfifo_center: after: shift_dq[%u]=%d \n " ,
i , shift_dq ) ;
if ( write )
scc_mgr_set_dq_out1_delay ( i ,
temp_dq_io_delay1 + shift_dq ) ;
else
scc_mgr_set_dq_in_delay ( p ,
temp_dq_io_delay1 + shift_dq ) ;
scc_mgr_load_dq ( p ) ;
debug_cond ( DLEVEL = = 2 ,
" vfifo_center: margin[%u]=[%d,%d] \n " , i ,
left_edge [ i ] - shift_dq + ( - mid_min ) ,
right_edge [ i ] + shift_dq - ( - mid_min ) ) ;
/* To determine values for export structures */
if ( left_edge [ i ] - shift_dq + ( - mid_min ) < * dq_margin )
* dq_margin = left_edge [ i ] - shift_dq + ( - mid_min ) ;
if ( right_edge [ i ] + shift_dq - ( - mid_min ) < * dqs_margin )
* dqs_margin = right_edge [ i ] + shift_dq - ( - mid_min ) ;
}
}
/**
* rw_mgr_mem_calibrate_vfifo_center ( ) - Per - bit deskew DQ and centering
* @ rank_bgn : Rank number
* @ rw_group : Read / Write Group
* @ test_bgn : Rank at which the test begins
* @ use_read_test : Perform a read test
* @ update_fom : Update FOM
*
* Per - bit deskew DQ and centering .
*/
static int rw_mgr_mem_calibrate_vfifo_center ( const u32 rank_bgn ,
const u32 rw_group , const u32 test_bgn ,
const int use_read_test , const int update_fom )
{
const u32 addr =
SDR_PHYGRP_SCCGRP_ADDRESS + SCC_MGR_DQS_IN_DELAY_OFFSET +
( rw_group < < 2 ) ;
/*
* Store these as signed since there are comparisons with
* signed numbers .
*/
u32 sticky_bit_chk ;
int32_t left_edge [ rwcfg - > mem_dq_per_read_dqs ] ;
int32_t right_edge [ rwcfg - > mem_dq_per_read_dqs ] ;
int32_t orig_mid_min , mid_min ;
int32_t new_dqs , start_dqs , start_dqs_en = 0 , final_dqs_en ;
int32_t dq_margin , dqs_margin ;
int i , min_index ;
int ret ;
debug ( " %s:%d: %u %u " , __func__ , __LINE__ , rw_group , test_bgn ) ;
start_dqs = readl ( addr ) ;
if ( iocfg - > shift_dqs_en_when_shift_dqs )
start_dqs_en = readl ( addr - iocfg - > dqs_en_delay_offset ) ;
/* set the left and right edge of each bit to an illegal value */
/* use (iocfg->io_in_delay_max + 1) as an illegal value */
sticky_bit_chk = 0 ;
for ( i = 0 ; i < rwcfg - > mem_dq_per_read_dqs ; i + + ) {
left_edge [ i ] = iocfg - > io_in_delay_max + 1 ;
right_edge [ i ] = iocfg - > io_in_delay_max + 1 ;
}
/* Search for the left edge of the window for each bit */
search_left_edge ( 0 , rank_bgn , rw_group , rw_group , test_bgn ,
& sticky_bit_chk ,
left_edge , right_edge , use_read_test ) ;
/* Search for the right edge of the window for each bit */
ret = search_right_edge ( 0 , rank_bgn , rw_group , rw_group ,
start_dqs , start_dqs_en ,
& sticky_bit_chk ,
left_edge , right_edge , use_read_test ) ;
if ( ret ) {
/*
* Restore delay chain settings before letting the loop
* in rw_mgr_mem_calibrate_vfifo to retry different
* dqs / ck relationships .
*/
scc_mgr_set_dqs_bus_in_delay ( rw_group , start_dqs ) ;
if ( iocfg - > shift_dqs_en_when_shift_dqs )
scc_mgr_set_dqs_en_delay ( rw_group , start_dqs_en ) ;
scc_mgr_load_dqs ( rw_group ) ;
writel ( 0 , & sdr_scc_mgr - > update ) ;
debug_cond ( DLEVEL = = 1 ,
" %s:%d vfifo_center: failed to find edge [%u]: %d %d " ,
__func__ , __LINE__ , i , left_edge [ i ] , right_edge [ i ] ) ;
if ( use_read_test ) {
set_failing_group_stage ( rw_group *
rwcfg - > mem_dq_per_read_dqs + i ,
CAL_STAGE_VFIFO ,
CAL_SUBSTAGE_VFIFO_CENTER ) ;
} else {
set_failing_group_stage ( rw_group *
rwcfg - > mem_dq_per_read_dqs + i ,
CAL_STAGE_VFIFO_AFTER_WRITES ,
CAL_SUBSTAGE_VFIFO_CENTER ) ;
}
return - EIO ;
}
min_index = get_window_mid_index ( 0 , left_edge , right_edge , & mid_min ) ;
/* Determine the amount we can change DQS (which is -mid_min) */
orig_mid_min = mid_min ;
new_dqs = start_dqs - mid_min ;
if ( new_dqs > iocfg - > dqs_in_delay_max )
new_dqs = iocfg - > dqs_in_delay_max ;
else if ( new_dqs < 0 )
new_dqs = 0 ;
mid_min = start_dqs - new_dqs ;
debug_cond ( DLEVEL = = 1 , " vfifo_center: new mid_min=%d new_dqs=%d \n " ,
mid_min , new_dqs ) ;
if ( iocfg - > shift_dqs_en_when_shift_dqs ) {
if ( start_dqs_en - mid_min > iocfg - > dqs_en_delay_max )
mid_min + = start_dqs_en - mid_min -
iocfg - > dqs_en_delay_max ;
else if ( start_dqs_en - mid_min < 0 )
mid_min + = start_dqs_en - mid_min ;
}
new_dqs = start_dqs - mid_min ;
debug_cond ( DLEVEL = = 1 ,
" vfifo_center: start_dqs=%d start_dqs_en=%d new_dqs=%d mid_min=%d \n " ,
start_dqs ,
iocfg - > shift_dqs_en_when_shift_dqs ? start_dqs_en : - 1 ,
new_dqs , mid_min ) ;
/* Add delay to bring centre of all DQ windows to the same "level". */
center_dq_windows ( 0 , left_edge , right_edge , mid_min , orig_mid_min ,
min_index , test_bgn , & dq_margin , & dqs_margin ) ;
/* Move DQS-en */
if ( iocfg - > shift_dqs_en_when_shift_dqs ) {
final_dqs_en = start_dqs_en - mid_min ;
scc_mgr_set_dqs_en_delay ( rw_group , final_dqs_en ) ;
scc_mgr_load_dqs ( rw_group ) ;
}
/* Move DQS */
scc_mgr_set_dqs_bus_in_delay ( rw_group , new_dqs ) ;
scc_mgr_load_dqs ( rw_group ) ;
debug_cond ( DLEVEL = = 2 ,
" %s:%d vfifo_center: dq_margin=%d dqs_margin=%d " ,
__func__ , __LINE__ , dq_margin , dqs_margin ) ;
/*
* Do not remove this line as it makes sure all of our decisions
* have been applied . Apply the update bit .
*/
writel ( 0 , & sdr_scc_mgr - > update ) ;
if ( ( dq_margin < 0 ) | | ( dqs_margin < 0 ) )
return - EINVAL ;
return 0 ;
}
/**
* rw_mgr_mem_calibrate_guaranteed_write ( ) - Perform guaranteed write into the device
* @ rw_group : Read / Write Group
* @ phase : DQ / DQS phase
*
* Because initially no communication ca be reliably performed with the memory
* device , the sequencer uses a guaranteed write mechanism to write data into
* the memory device .
*/
static int rw_mgr_mem_calibrate_guaranteed_write ( const u32 rw_group ,
const u32 phase )
{
int ret ;
/* Set a particular DQ/DQS phase. */
scc_mgr_set_dqdqs_output_phase_all_ranks ( rw_group , phase ) ;
debug_cond ( DLEVEL = = 1 , " %s:%d guaranteed write: g=%u p=%u \n " ,
__func__ , __LINE__ , rw_group , phase ) ;
/*
* Altera EMI_RM 2015.05 .04 : : Figure 1 - 25
* Load up the patterns used by read calibration using the
* current DQDQS phase .
*/
rw_mgr_mem_calibrate_read_load_patterns ( 0 , 1 ) ;
if ( gbl - > phy_debug_mode_flags & PHY_DEBUG_DISABLE_GUARANTEED_READ )
return 0 ;
/*
* Altera EMI_RM 2015.05 .04 : : Figure 1 - 26
* Back - to - Back reads of the patterns used for calibration .
*/
ret = rw_mgr_mem_calibrate_read_test_patterns ( 0 , rw_group , 1 ) ;
if ( ret )
debug_cond ( DLEVEL = = 1 ,
" %s:%d Guaranteed read test failed: g=%u p=%u \n " ,
__func__ , __LINE__ , rw_group , phase ) ;
return ret ;
}
/**
* rw_mgr_mem_calibrate_dqs_enable_calibration ( ) - DQS Enable Calibration
* @ rw_group : Read / Write Group
* @ test_bgn : Rank at which the test begins
*
* DQS enable calibration ensures reliable capture of the DQ signal without
* glitches on the DQS line .
*/
static int rw_mgr_mem_calibrate_dqs_enable_calibration ( const u32 rw_group ,
const u32 test_bgn )
{
/*
* Altera EMI_RM 2015.05 .04 : : Figure 1 - 27
* DQS and DQS Eanble Signal Relationships .
*/
/* We start at zero, so have one less dq to devide among */
const u32 delay_step = iocfg - > io_in_delay_max /
( rwcfg - > mem_dq_per_read_dqs - 1 ) ;
int ret ;
u32 i , p , d , r ;
debug ( " %s:%d (%u,%u) \n " , __func__ , __LINE__ , rw_group , test_bgn ) ;
/* Try different dq_in_delays since the DQ path is shorter than DQS. */
for ( r = 0 ; r < rwcfg - > mem_number_of_ranks ;
r + = NUM_RANKS_PER_SHADOW_REG ) {
for ( i = 0 , p = test_bgn , d = 0 ;
i < rwcfg - > mem_dq_per_read_dqs ;
i + + , p + + , d + = delay_step ) {
debug_cond ( DLEVEL = = 1 ,
" %s:%d: g=%u r=%u i=%u p=%u d=%u \n " ,
__func__ , __LINE__ , rw_group , r , i , p , d ) ;
scc_mgr_set_dq_in_delay ( p , d ) ;
scc_mgr_load_dq ( p ) ;
}
writel ( 0 , & sdr_scc_mgr - > update ) ;
}
/*
* Try rw_mgr_mem_calibrate_vfifo_find_dqs_en_phase across different
* dq_in_delay values
*/
ret = rw_mgr_mem_calibrate_vfifo_find_dqs_en_phase ( rw_group ) ;
debug_cond ( DLEVEL = = 1 ,
" %s:%d: g=%u found=%u; Reseting delay chain to zero \n " ,
__func__ , __LINE__ , rw_group , ! ret ) ;
for ( r = 0 ; r < rwcfg - > mem_number_of_ranks ;
r + = NUM_RANKS_PER_SHADOW_REG ) {
scc_mgr_apply_group_dq_in_delay ( test_bgn , 0 ) ;
writel ( 0 , & sdr_scc_mgr - > update ) ;
}
return ret ;
}
/**
* rw_mgr_mem_calibrate_dq_dqs_centering ( ) - Centering DQ / DQS
* @ rw_group : Read / Write Group
* @ test_bgn : Rank at which the test begins
* @ use_read_test : Perform a read test
* @ update_fom : Update FOM
*
* The centerin DQ / DQS stage attempts to align DQ and DQS signals on reads
* within a group .
*/
static int
rw_mgr_mem_calibrate_dq_dqs_centering ( const u32 rw_group , const u32 test_bgn ,
const int use_read_test ,
const int update_fom )
{
int ret , grp_calibrated ;
u32 rank_bgn , sr ;
/*
* Altera EMI_RM 2015.05 .04 : : Figure 1 - 28
* Read per - bit deskew can be done on a per shadow register basis .
*/
grp_calibrated = 1 ;
for ( rank_bgn = 0 , sr = 0 ;
rank_bgn < rwcfg - > mem_number_of_ranks ;
rank_bgn + = NUM_RANKS_PER_SHADOW_REG , sr + + ) {
ret = rw_mgr_mem_calibrate_vfifo_center ( rank_bgn , rw_group ,
test_bgn ,
use_read_test ,
update_fom ) ;
if ( ! ret )
continue ;
grp_calibrated = 0 ;
}
if ( ! grp_calibrated )
return - EIO ;
return 0 ;
}
/**
* rw_mgr_mem_calibrate_vfifo ( ) - Calibrate the read valid prediction FIFO
* @ rw_group : Read / Write Group
* @ test_bgn : Rank at which the test begins
*
* Stage 1 : Calibrate the read valid prediction FIFO .
*
* This function implements UniPHY calibration Stage 1 , as explained in
* detail in Altera EMI_RM 2015.05 .04 , " UniPHY Calibration Stages " .
*
* - read valid prediction will consist of finding :
* - DQS enable phase and DQS enable delay ( DQS Enable Calibration )
* - DQS input phase and DQS input delay ( DQ / DQS Centering )
* - we also do a per - bit deskew on the DQ lines .
*/
static int rw_mgr_mem_calibrate_vfifo ( const u32 rw_group , const u32 test_bgn )
{
u32 p , d ;
u32 dtaps_per_ptap ;
u32 failed_substage ;
int ret ;
debug ( " %s:%d: %u %u \n " , __func__ , __LINE__ , rw_group , test_bgn ) ;
/* Update info for sims */
reg_file_set_group ( rw_group ) ;
reg_file_set_stage ( CAL_STAGE_VFIFO ) ;
reg_file_set_sub_stage ( CAL_SUBSTAGE_GUARANTEED_READ ) ;
failed_substage = CAL_SUBSTAGE_GUARANTEED_READ ;
/* USER Determine number of delay taps for each phase tap. */
dtaps_per_ptap = DIV_ROUND_UP ( iocfg - > delay_per_opa_tap ,
iocfg - > delay_per_dqs_en_dchain_tap ) - 1 ;
for ( d = 0 ; d < = dtaps_per_ptap ; d + = 2 ) {
/*
* In RLDRAMX we may be messing the delay of pins in
* the same write rw_group but outside of the current read
* the rw_group , but that ' s ok because we haven ' t calibrated
* output side yet .
*/
if ( d > 0 ) {
scc_mgr_apply_group_all_out_delay_add_all_ranks (
rw_group , d ) ;
}
for ( p = 0 ; p < = iocfg - > dqdqs_out_phase_max ; p + + ) {
/* 1) Guaranteed Write */
ret = rw_mgr_mem_calibrate_guaranteed_write ( rw_group , p ) ;
if ( ret )
break ;
/* 2) DQS Enable Calibration */
ret = rw_mgr_mem_calibrate_dqs_enable_calibration ( rw_group ,
test_bgn ) ;
if ( ret ) {
failed_substage = CAL_SUBSTAGE_DQS_EN_PHASE ;
continue ;
}
/* 3) Centering DQ/DQS */
/*
* If doing read after write calibration , do not update
* FOM now . Do it then .
*/
ret = rw_mgr_mem_calibrate_dq_dqs_centering ( rw_group ,
test_bgn , 1 , 0 ) ;
if ( ret ) {
failed_substage = CAL_SUBSTAGE_VFIFO_CENTER ;
continue ;
}
/* All done. */
goto cal_done_ok ;
}
}
/* Calibration Stage 1 failed. */
set_failing_group_stage ( rw_group , CAL_STAGE_VFIFO , failed_substage ) ;
return 0 ;
/* Calibration Stage 1 completed OK. */
cal_done_ok :
/*
* Reset the delay chains back to zero if they have moved > 1
* ( check for > 1 because loop will increase d even when pass in
* first case ) .
*/
if ( d > 2 )
scc_mgr_zero_group ( rw_group , 1 ) ;
return 1 ;
}
/**
* rw_mgr_mem_calibrate_vfifo_end ( ) - DQ / DQS Centering .
* @ rw_group : Read / Write Group
* @ test_bgn : Rank at which the test begins
*
* Stage 3 : DQ / DQS Centering .
*
* This function implements UniPHY calibration Stage 3 , as explained in
* detail in Altera EMI_RM 2015.05 .04 , " UniPHY Calibration Stages " .
*/
static int rw_mgr_mem_calibrate_vfifo_end ( const u32 rw_group ,
const u32 test_bgn )
{
int ret ;
debug ( " %s:%d %u %u " , __func__ , __LINE__ , rw_group , test_bgn ) ;
/* Update info for sims. */
reg_file_set_group ( rw_group ) ;
reg_file_set_stage ( CAL_STAGE_VFIFO_AFTER_WRITES ) ;
reg_file_set_sub_stage ( CAL_SUBSTAGE_VFIFO_CENTER ) ;
ret = rw_mgr_mem_calibrate_dq_dqs_centering ( rw_group , test_bgn , 0 , 1 ) ;
if ( ret )
set_failing_group_stage ( rw_group ,
CAL_STAGE_VFIFO_AFTER_WRITES ,
CAL_SUBSTAGE_VFIFO_CENTER ) ;
return ret ;
}
/**
* rw_mgr_mem_calibrate_lfifo ( ) - Minimize latency
*
* Stage 4 : Minimize latency .
*
* This function implements UniPHY calibration Stage 4 , as explained in
* detail in Altera EMI_RM 2015.05 .04 , " UniPHY Calibration Stages " .
* Calibrate LFIFO to find smallest read latency .
*/
static u32 rw_mgr_mem_calibrate_lfifo ( void )
{
int found_one = 0 ;
debug ( " %s:%d \n " , __func__ , __LINE__ ) ;
/* Update info for sims. */
reg_file_set_stage ( CAL_STAGE_LFIFO ) ;
reg_file_set_sub_stage ( CAL_SUBSTAGE_READ_LATENCY ) ;
/* Load up the patterns used by read calibration for all ranks */
rw_mgr_mem_calibrate_read_load_patterns ( 0 , 1 ) ;
do {
writel ( gbl - > curr_read_lat , & phy_mgr_cfg - > phy_rlat ) ;
debug_cond ( DLEVEL = = 2 , " %s:%d lfifo: read_lat=%u " ,
__func__ , __LINE__ , gbl - > curr_read_lat ) ;
if ( ! rw_mgr_mem_calibrate_read_test_all_ranks ( 0 , NUM_READ_TESTS ,
PASS_ALL_BITS , 1 ) )
break ;
found_one = 1 ;
/*
* Reduce read latency and see if things are
* working correctly .
*/
gbl - > curr_read_lat - - ;
} while ( gbl - > curr_read_lat > 0 ) ;
/* Reset the fifos to get pointers to known state. */
writel ( 0 , & phy_mgr_cmd - > fifo_reset ) ;
if ( found_one ) {
/* Add a fudge factor to the read latency that was determined */
gbl - > curr_read_lat + = 2 ;
writel ( gbl - > curr_read_lat , & phy_mgr_cfg - > phy_rlat ) ;
debug_cond ( DLEVEL = = 2 ,
" %s:%d lfifo: success: using read_lat=%u \n " ,
__func__ , __LINE__ , gbl - > curr_read_lat ) ;
} else {
set_failing_group_stage ( 0xff , CAL_STAGE_LFIFO ,
CAL_SUBSTAGE_READ_LATENCY ) ;
debug_cond ( DLEVEL = = 2 ,
" %s:%d lfifo: failed at initial read_lat=%u \n " ,
__func__ , __LINE__ , gbl - > curr_read_lat ) ;
}
return found_one ;
}
/**
* search_window ( ) - Search for the / part of the window with DM / DQS shift
* @ search_dm : If 1 , search for the DM shift , if 0 , search for DQS shift
* @ rank_bgn : Rank number
* @ write_group : Write Group
* @ bgn_curr : Current window begin
* @ end_curr : Current window end
* @ bgn_best : Current best window begin
* @ end_best : Current best window end
* @ win_best : Size of the best window
* @ new_dqs : New DQS value ( only applicable if search_dm = 0 ) .
*
* Search for the / part of the window with DM / DQS shift .
*/
static void search_window ( const int search_dm ,
const u32 rank_bgn , const u32 write_group ,
int * bgn_curr , int * end_curr , int * bgn_best ,
int * end_best , int * win_best , int new_dqs )
{
u32 bit_chk ;
const int max = iocfg - > io_out1_delay_max - new_dqs ;
int d , di ;
/* Search for the/part of the window with DM/DQS shift. */
for ( di = max ; di > = 0 ; di - = DELTA_D ) {
if ( search_dm ) {
d = di ;
scc_mgr_apply_group_dm_out1_delay ( d ) ;
} else {
/* For DQS, we go from 0...max */
d = max - di ;
/*
* Note : This only shifts DQS , so are we limiting
* ourselves to width of DQ unnecessarily .
*/
scc_mgr_apply_group_dqs_io_and_oct_out1 ( write_group ,
d + new_dqs ) ;
}
writel ( 0 , & sdr_scc_mgr - > update ) ;
if ( rw_mgr_mem_calibrate_write_test ( rank_bgn , write_group , 1 ,
PASS_ALL_BITS , & bit_chk ,
0 ) ) {
/* Set current end of the window. */
* end_curr = search_dm ? - d : d ;
/*
* If a starting edge of our window has not been seen
* this is our current start of the DM window .
*/
if ( * bgn_curr = = iocfg - > io_out1_delay_max + 1 )
* bgn_curr = search_dm ? - d : d ;
/*
* If current window is bigger than best seen .
* Set best seen to be current window .
*/
if ( ( * end_curr - * bgn_curr + 1 ) > * win_best ) {
* win_best = * end_curr - * bgn_curr + 1 ;
* bgn_best = * bgn_curr ;
* end_best = * end_curr ;
}
} else {
/* We just saw a failing test. Reset temp edge. */
* bgn_curr = iocfg - > io_out1_delay_max + 1 ;
* end_curr = iocfg - > io_out1_delay_max + 1 ;
/* Early exit is only applicable to DQS. */
if ( search_dm )
continue ;
/*
* Early exit optimization : if the remaining delay
* chain space is less than already seen largest
* window we can exit .
*/
if ( * win_best - 1 > iocfg - > io_out1_delay_max - new_dqs - d )
break ;
}
}
}
/*
* rw_mgr_mem_calibrate_writes_center ( ) - Center all windows
* @ rank_bgn : Rank number
* @ write_group : Write group
* @ test_bgn : Rank at which the test begins
*
* Center all windows . Do per - bit - deskew to possibly increase size of
* certain windows .
*/
static int
rw_mgr_mem_calibrate_writes_center ( const u32 rank_bgn , const u32 write_group ,
const u32 test_bgn )
{
int i ;
u32 sticky_bit_chk ;
u32 min_index ;
int left_edge [ rwcfg - > mem_dq_per_write_dqs ] ;
int right_edge [ rwcfg - > mem_dq_per_write_dqs ] ;
int mid ;
int mid_min , orig_mid_min ;
int new_dqs , start_dqs ;
int dq_margin , dqs_margin , dm_margin ;
int bgn_curr = iocfg - > io_out1_delay_max + 1 ;
int end_curr = iocfg - > io_out1_delay_max + 1 ;
int bgn_best = iocfg - > io_out1_delay_max + 1 ;
int end_best = iocfg - > io_out1_delay_max + 1 ;
int win_best = 0 ;
int ret ;
debug ( " %s:%d %u %u " , __func__ , __LINE__ , write_group , test_bgn ) ;
dm_margin = 0 ;
start_dqs = readl ( ( SDR_PHYGRP_SCCGRP_ADDRESS |
SCC_MGR_IO_OUT1_DELAY_OFFSET ) +
( rwcfg - > mem_dq_per_write_dqs < < 2 ) ) ;
/* Per-bit deskew. */
/*
* Set the left and right edge of each bit to an illegal value .
* Use ( iocfg - > io_out1_delay_max + 1 ) as an illegal value .
*/
sticky_bit_chk = 0 ;
for ( i = 0 ; i < rwcfg - > mem_dq_per_write_dqs ; i + + ) {
left_edge [ i ] = iocfg - > io_out1_delay_max + 1 ;
right_edge [ i ] = iocfg - > io_out1_delay_max + 1 ;
}
/* Search for the left edge of the window for each bit. */
search_left_edge ( 1 , rank_bgn , write_group , 0 , test_bgn ,
& sticky_bit_chk ,
left_edge , right_edge , 0 ) ;
/* Search for the right edge of the window for each bit. */
ret = search_right_edge ( 1 , rank_bgn , write_group , 0 ,
start_dqs , 0 ,
& sticky_bit_chk ,
left_edge , right_edge , 0 ) ;
if ( ret ) {
set_failing_group_stage ( test_bgn + ret - 1 , CAL_STAGE_WRITES ,
CAL_SUBSTAGE_WRITES_CENTER ) ;
return - EINVAL ;
}
min_index = get_window_mid_index ( 1 , left_edge , right_edge , & mid_min ) ;
/* Determine the amount we can change DQS (which is -mid_min). */
orig_mid_min = mid_min ;
new_dqs = start_dqs ;
mid_min = 0 ;
debug_cond ( DLEVEL = = 1 ,
" %s:%d write_center: start_dqs=%d new_dqs=%d mid_min=%d \n " ,
__func__ , __LINE__ , start_dqs , new_dqs , mid_min ) ;
/* Add delay to bring centre of all DQ windows to the same "level". */
center_dq_windows ( 1 , left_edge , right_edge , mid_min , orig_mid_min ,
min_index , 0 , & dq_margin , & dqs_margin ) ;
/* Move DQS */
scc_mgr_apply_group_dqs_io_and_oct_out1 ( write_group , new_dqs ) ;
writel ( 0 , & sdr_scc_mgr - > update ) ;
/* Centre DM */
debug_cond ( DLEVEL = = 2 , " %s:%d write_center: DM \n " , __func__ , __LINE__ ) ;
/*
* Set the left and right edge of each bit to an illegal value .
* Use ( iocfg - > io_out1_delay_max + 1 ) as an illegal value .
*/
left_edge [ 0 ] = iocfg - > io_out1_delay_max + 1 ;
right_edge [ 0 ] = iocfg - > io_out1_delay_max + 1 ;
/* Search for the/part of the window with DM shift. */
search_window ( 1 , rank_bgn , write_group , & bgn_curr , & end_curr ,
& bgn_best , & end_best , & win_best , 0 ) ;
/* Reset DM delay chains to 0. */
scc_mgr_apply_group_dm_out1_delay ( 0 ) ;
/*
* Check to see if the current window nudges up aganist 0 delay .
* If so we need to continue the search by shifting DQS otherwise DQS
* search begins as a new search .
*/
if ( end_curr ! = 0 ) {
bgn_curr = iocfg - > io_out1_delay_max + 1 ;
end_curr = iocfg - > io_out1_delay_max + 1 ;
}
/* Search for the/part of the window with DQS shifts. */
search_window ( 0 , rank_bgn , write_group , & bgn_curr , & end_curr ,
& bgn_best , & end_best , & win_best , new_dqs ) ;
/* Assign left and right edge for cal and reporting. */
left_edge [ 0 ] = - 1 * bgn_best ;
right_edge [ 0 ] = end_best ;
debug_cond ( DLEVEL = = 2 , " %s:%d dm_calib: left=%d right=%d \n " ,
__func__ , __LINE__ , left_edge [ 0 ] , right_edge [ 0 ] ) ;
/* Move DQS (back to orig). */
scc_mgr_apply_group_dqs_io_and_oct_out1 ( write_group , new_dqs ) ;
/* Move DM */
/* Find middle of window for the DM bit. */
mid = ( left_edge [ 0 ] - right_edge [ 0 ] ) / 2 ;
/* Only move right, since we are not moving DQS/DQ. */
if ( mid < 0 )
mid = 0 ;
/* dm_marign should fail if we never find a window. */
if ( win_best = = 0 )
dm_margin = - 1 ;
else
dm_margin = left_edge [ 0 ] - mid ;
scc_mgr_apply_group_dm_out1_delay ( mid ) ;
writel ( 0 , & sdr_scc_mgr - > update ) ;
debug_cond ( DLEVEL = = 2 ,
" %s:%d dm_calib: left=%d right=%d mid=%d dm_margin=%d \n " ,
__func__ , __LINE__ , left_edge [ 0 ] , right_edge [ 0 ] ,
mid , dm_margin ) ;
/* Export values. */
gbl - > fom_out + = dq_margin + dqs_margin ;
debug_cond ( DLEVEL = = 2 ,
" %s:%d write_center: dq_margin=%d dqs_margin=%d dm_margin=%d \n " ,
__func__ , __LINE__ , dq_margin , dqs_margin , dm_margin ) ;
/*
* Do not remove this line as it makes sure all of our
* decisions have been applied .
*/
writel ( 0 , & sdr_scc_mgr - > update ) ;
if ( ( dq_margin < 0 ) | | ( dqs_margin < 0 ) | | ( dm_margin < 0 ) )
return - EINVAL ;
return 0 ;
}
/**
* rw_mgr_mem_calibrate_writes ( ) - Write Calibration Part One
* @ rank_bgn : Rank number
* @ group : Read / Write Group
* @ test_bgn : Rank at which the test begins
*
* Stage 2 : Write Calibration Part One .
*
* This function implements UniPHY calibration Stage 2 , as explained in
* detail in Altera EMI_RM 2015.05 .04 , " UniPHY Calibration Stages " .
*/
static int rw_mgr_mem_calibrate_writes ( const u32 rank_bgn , const u32 group ,
const u32 test_bgn )
{
int ret ;
/* Update info for sims */
debug ( " %s:%d %u %u \n " , __func__ , __LINE__ , group , test_bgn ) ;
reg_file_set_group ( group ) ;
reg_file_set_stage ( CAL_STAGE_WRITES ) ;
reg_file_set_sub_stage ( CAL_SUBSTAGE_WRITES_CENTER ) ;
ret = rw_mgr_mem_calibrate_writes_center ( rank_bgn , group , test_bgn ) ;
if ( ret )
set_failing_group_stage ( group , CAL_STAGE_WRITES ,
CAL_SUBSTAGE_WRITES_CENTER ) ;
return ret ;
}
/**
* mem_precharge_and_activate ( ) - Precharge all banks and activate
*
* Precharge all banks and activate row 0 in bank " 000... " and bank " 111... " .
*/
static void mem_precharge_and_activate ( void )
{
int r ;
for ( r = 0 ; r < rwcfg - > mem_number_of_ranks ; r + + ) {
/* Set rank. */
set_rank_and_odt_mask ( r , RW_MGR_ODT_MODE_OFF ) ;
/* Precharge all banks. */
writel ( rwcfg - > precharge_all , SDR_PHYGRP_RWMGRGRP_ADDRESS |
RW_MGR_RUN_SINGLE_GROUP_OFFSET ) ;
writel ( 0x0F , & sdr_rw_load_mgr_regs - > load_cntr0 ) ;
writel ( rwcfg - > activate_0_and_1_wait1 ,
& sdr_rw_load_jump_mgr_regs - > load_jump_add0 ) ;
writel ( 0x0F , & sdr_rw_load_mgr_regs - > load_cntr1 ) ;
writel ( rwcfg - > activate_0_and_1_wait2 ,
& sdr_rw_load_jump_mgr_regs - > load_jump_add1 ) ;
/* Activate rows. */
writel ( rwcfg - > activate_0_and_1 , SDR_PHYGRP_RWMGRGRP_ADDRESS |
RW_MGR_RUN_SINGLE_GROUP_OFFSET ) ;
}
}
/**
* mem_init_latency ( ) - Configure memory RLAT and WLAT settings
*
* Configure memory RLAT and WLAT parameters .
*/
static void mem_init_latency ( void )
{
/*
* For AV / CV , LFIFO is hardened and always runs at full rate
* so max latency in AFI clocks , used here , is correspondingly
* smaller .
*/
const u32 max_latency = ( 1 < < misccfg - > max_latency_count_width ) - 1 ;
u32 rlat , wlat ;
debug ( " %s:%d \n " , __func__ , __LINE__ ) ;
/*
* Read in write latency .
* WL for Hard PHY does not include additive latency .
*/
wlat = readl ( & data_mgr - > t_wl_add ) ;
wlat + = readl ( & data_mgr - > mem_t_add ) ;
gbl - > rw_wl_nop_cycles = wlat - 1 ;
/* Read in readl latency. */
rlat = readl ( & data_mgr - > t_rl_add ) ;
/* Set a pretty high read latency initially. */
gbl - > curr_read_lat = rlat + 16 ;
if ( gbl - > curr_read_lat > max_latency )
gbl - > curr_read_lat = max_latency ;
writel ( gbl - > curr_read_lat , & phy_mgr_cfg - > phy_rlat ) ;
/* Advertise write latency. */
writel ( wlat , & phy_mgr_cfg - > afi_wlat ) ;
}
/**
* @ mem_skip_calibrate ( ) - Set VFIFO and LFIFO to instant - on settings
*
* Set VFIFO and LFIFO to instant - on settings in skip calibration mode .
*/
static void mem_skip_calibrate ( void )
{
u32 vfifo_offset ;
u32 i , j , r ;
debug ( " %s:%d \n " , __func__ , __LINE__ ) ;
/* Need to update every shadow register set used by the interface */
for ( r = 0 ; r < rwcfg - > mem_number_of_ranks ;
r + = NUM_RANKS_PER_SHADOW_REG ) {
/*
* Set output phase alignment settings appropriate for
* skip calibration .
*/
for ( i = 0 ; i < rwcfg - > mem_if_read_dqs_width ; i + + ) {
scc_mgr_set_dqs_en_phase ( i , 0 ) ;
if ( iocfg - > dll_chain_length = = 6 )
scc_mgr_set_dqdqs_output_phase ( i , 6 ) ;
else
scc_mgr_set_dqdqs_output_phase ( i , 7 ) ;
/*
* Case : 33398
*
* Write data arrives to the I / O two cycles before write
* latency is reached ( 720 deg ) .
* - > due to bit - slip in a / c bus
* - > to allow board skew where dqs is longer than ck
* - > how often can this happen ! ?
* - > can claim back some ptaps for high freq
* support if we can relax this , but i digress . . .
*
* The write_clk leads mem_ck by 90 deg
* The minimum ptap of the OPA is 180 deg
* Each ptap has ( 360 / IO_DLL_CHAIN_LENGH ) deg of delay
* The write_clk is always delayed by 2 ptaps
*
* Hence , to make DQS aligned to CK , we need to delay
* DQS by :
* ( 720 - 90 - 180 - 2 ) *
* ( 360 / iocfg - > dll_chain_length )
*
* Dividing the above by ( 360 / iocfg - > dll_chain_length )
* gives us the number of ptaps , which simplies to :
*
* ( 1.25 * iocfg - > dll_chain_length - 2 )
*/
scc_mgr_set_dqdqs_output_phase ( i ,
( ( 125 * iocfg - > dll_chain_length ) / 100 ) - 2 ) ;
}
writel ( 0xff , & sdr_scc_mgr - > dqs_ena ) ;
writel ( 0xff , & sdr_scc_mgr - > dqs_io_ena ) ;
for ( i = 0 ; i < rwcfg - > mem_if_write_dqs_width ; i + + ) {
writel ( i , SDR_PHYGRP_SCCGRP_ADDRESS |
SCC_MGR_GROUP_COUNTER_OFFSET ) ;
}
writel ( 0xff , & sdr_scc_mgr - > dq_ena ) ;
writel ( 0xff , & sdr_scc_mgr - > dm_ena ) ;
writel ( 0 , & sdr_scc_mgr - > update ) ;
}
/* Compensate for simulation model behaviour */
for ( i = 0 ; i < rwcfg - > mem_if_read_dqs_width ; i + + ) {
scc_mgr_set_dqs_bus_in_delay ( i , 10 ) ;
scc_mgr_load_dqs ( i ) ;
}
writel ( 0 , & sdr_scc_mgr - > update ) ;
/*
* ArriaV has hard FIFOs that can only be initialized by incrementing
* in sequencer .
*/
vfifo_offset = misccfg - > calib_vfifo_offset ;
for ( j = 0 ; j < vfifo_offset ; j + + )
writel ( 0xff , & phy_mgr_cmd - > inc_vfifo_hard_phy ) ;
writel ( 0 , & phy_mgr_cmd - > fifo_reset ) ;
/*
* For Arria V and Cyclone V with hard LFIFO , we get the skip - cal
* setting from generation - time constant .
*/
gbl - > curr_read_lat = misccfg - > calib_lfifo_offset ;
writel ( gbl - > curr_read_lat , & phy_mgr_cfg - > phy_rlat ) ;
}
/**
* mem_calibrate ( ) - Memory calibration entry point .
*
* Perform memory calibration .
*/
static u32 mem_calibrate ( void )
{
u32 i ;
u32 rank_bgn , sr ;
u32 write_group , write_test_bgn ;
u32 read_group , read_test_bgn ;
u32 run_groups , current_run ;
u32 failing_groups = 0 ;
u32 group_failed = 0 ;
const u32 rwdqs_ratio = rwcfg - > mem_if_read_dqs_width /
rwcfg - > mem_if_write_dqs_width ;
debug ( " %s:%d \n " , __func__ , __LINE__ ) ;
/* Initialize the data settings */
gbl - > error_substage = CAL_SUBSTAGE_NIL ;
gbl - > error_stage = CAL_STAGE_NIL ;
gbl - > error_group = 0xff ;
gbl - > fom_in = 0 ;
gbl - > fom_out = 0 ;
/* Initialize WLAT and RLAT. */
mem_init_latency ( ) ;
/* Initialize bit slips. */
mem_precharge_and_activate ( ) ;
for ( i = 0 ; i < rwcfg - > mem_if_read_dqs_width ; i + + ) {
writel ( i , SDR_PHYGRP_SCCGRP_ADDRESS |
SCC_MGR_GROUP_COUNTER_OFFSET ) ;
/* Only needed once to set all groups, pins, DQ, DQS, DM. */
if ( i = = 0 )
scc_mgr_set_hhp_extras ( ) ;
scc_set_bypass_mode ( i ) ;
}
/* Calibration is skipped. */
if ( ( dyn_calib_steps & CALIB_SKIP_ALL ) = = CALIB_SKIP_ALL ) {
/*
* Set VFIFO and LFIFO to instant - on settings in skip
* calibration mode .
*/
mem_skip_calibrate ( ) ;
/*
* Do not remove this line as it makes sure all of our
* decisions have been applied .
*/
writel ( 0 , & sdr_scc_mgr - > update ) ;
return 1 ;
}
/* Calibration is not skipped. */
for ( i = 0 ; i < NUM_CALIB_REPEAT ; i + + ) {
/*
* Zero all delay chain / phase settings for all
* groups and all shadow register sets .
*/
scc_mgr_zero_all ( ) ;
run_groups = ~ 0 ;
for ( write_group = 0 , write_test_bgn = 0 ; write_group
< rwcfg - > mem_if_write_dqs_width ; write_group + + ,
write_test_bgn + = rwcfg - > mem_dq_per_write_dqs ) {
/* Initialize the group failure */
group_failed = 0 ;
current_run = run_groups & ( ( 1 < <
RW_MGR_NUM_DQS_PER_WRITE_GROUP ) - 1 ) ;
run_groups = run_groups > >
RW_MGR_NUM_DQS_PER_WRITE_GROUP ;
if ( current_run = = 0 )
continue ;
writel ( write_group , SDR_PHYGRP_SCCGRP_ADDRESS |
SCC_MGR_GROUP_COUNTER_OFFSET ) ;
scc_mgr_zero_group ( write_group , 0 ) ;
for ( read_group = write_group * rwdqs_ratio ,
read_test_bgn = 0 ;
read_group < ( write_group + 1 ) * rwdqs_ratio ;
read_group + + ,
read_test_bgn + = rwcfg - > mem_dq_per_read_dqs ) {
if ( STATIC_CALIB_STEPS & CALIB_SKIP_VFIFO )
continue ;
/* Calibrate the VFIFO */
if ( rw_mgr_mem_calibrate_vfifo ( read_group ,
read_test_bgn ) )
continue ;
if ( ! ( gbl - > phy_debug_mode_flags &
PHY_DEBUG_SWEEP_ALL_GROUPS ) )
return 0 ;
/* The group failed, we're done. */
goto grp_failed ;
}
/* Calibrate the output side */
for ( rank_bgn = 0 , sr = 0 ;
rank_bgn < rwcfg - > mem_number_of_ranks ;
rank_bgn + = NUM_RANKS_PER_SHADOW_REG , sr + + ) {
if ( STATIC_CALIB_STEPS & CALIB_SKIP_WRITES )
continue ;
/* Not needed in quick mode! */
if ( STATIC_CALIB_STEPS &
CALIB_SKIP_DELAY_SWEEPS )
continue ;
/* Calibrate WRITEs */
if ( ! rw_mgr_mem_calibrate_writes ( rank_bgn ,
write_group ,
write_test_bgn ) )
continue ;
group_failed = 1 ;
if ( ! ( gbl - > phy_debug_mode_flags &
PHY_DEBUG_SWEEP_ALL_GROUPS ) )
return 0 ;
}
/* Some group failed, we're done. */
if ( group_failed )
goto grp_failed ;
for ( read_group = write_group * rwdqs_ratio ,
read_test_bgn = 0 ;
read_group < ( write_group + 1 ) * rwdqs_ratio ;
read_group + + ,
read_test_bgn + = rwcfg - > mem_dq_per_read_dqs ) {
if ( STATIC_CALIB_STEPS & CALIB_SKIP_WRITES )
continue ;
if ( ! rw_mgr_mem_calibrate_vfifo_end ( read_group ,
read_test_bgn ) )
continue ;
if ( ! ( gbl - > phy_debug_mode_flags &
PHY_DEBUG_SWEEP_ALL_GROUPS ) )
return 0 ;
/* The group failed, we're done. */
goto grp_failed ;
}
/* No group failed, continue as usual. */
continue ;
grp_failed : /* A group failed, increment the counter. */
failing_groups + + ;
}
/*
* USER If there are any failing groups then report
* the failure .
*/
if ( failing_groups ! = 0 )
return 0 ;
if ( STATIC_CALIB_STEPS & CALIB_SKIP_LFIFO )
continue ;
/* Calibrate the LFIFO */
if ( ! rw_mgr_mem_calibrate_lfifo ( ) )
return 0 ;
}
/*
* Do not remove this line as it makes sure all of our decisions
* have been applied .
*/
writel ( 0 , & sdr_scc_mgr - > update ) ;
return 1 ;
}
/**
* run_mem_calibrate ( ) - Perform memory calibration
*
* This function triggers the entire memory calibration procedure .
*/
static int run_mem_calibrate ( void )
{
int pass ;
debug ( " %s:%d \n " , __func__ , __LINE__ ) ;
/* Reset pass/fail status shown on afi_cal_success/fail */
writel ( PHY_MGR_CAL_RESET , & phy_mgr_cfg - > cal_status ) ;
/* Stop tracking manager. */
clrbits_le32 ( & sdr_ctrl - > ctrl_cfg , 1 < < 22 ) ;
phy_mgr_initialize ( ) ;
rw_mgr_mem_initialize ( ) ;
/* Perform the actual memory calibration. */
pass = mem_calibrate ( ) ;
mem_precharge_and_activate ( ) ;
writel ( 0 , & phy_mgr_cmd - > fifo_reset ) ;
/* Handoff. */
rw_mgr_mem_handoff ( ) ;
/*
* In Hard PHY this is a 2 - bit control :
* 0 : AFI Mux Select
* 1 : DDIO Mux Select
*/
writel ( 0x2 , & phy_mgr_cfg - > mux_sel ) ;
/* Start tracking manager. */
setbits_le32 ( & sdr_ctrl - > ctrl_cfg , 1 < < 22 ) ;
return pass ;
}
/**
* debug_mem_calibrate ( ) - Report result of memory calibration
* @ pass : Value indicating whether calibration passed or failed
*
* This function reports the results of the memory calibration
* and writes debug information into the register file .
*/
static void debug_mem_calibrate ( int pass )
{
u32 debug_info ;
if ( pass ) {
printf ( " %s: CALIBRATION PASSED \n " , __FILE__ ) ;
gbl - > fom_in / = 2 ;
gbl - > fom_out / = 2 ;
if ( gbl - > fom_in > 0xff )
gbl - > fom_in = 0xff ;
if ( gbl - > fom_out > 0xff )
gbl - > fom_out = 0xff ;
/* Update the FOM in the register file */
debug_info = gbl - > fom_in ;
debug_info | = gbl - > fom_out < < 8 ;
writel ( debug_info , & sdr_reg_file - > fom ) ;
writel ( debug_info , & phy_mgr_cfg - > cal_debug_info ) ;
writel ( PHY_MGR_CAL_SUCCESS , & phy_mgr_cfg - > cal_status ) ;
} else {
printf ( " %s: CALIBRATION FAILED \n " , __FILE__ ) ;
debug_info = gbl - > error_stage ;
debug_info | = gbl - > error_substage < < 8 ;
debug_info | = gbl - > error_group < < 16 ;
writel ( debug_info , & sdr_reg_file - > failing_stage ) ;
writel ( debug_info , & phy_mgr_cfg - > cal_debug_info ) ;
writel ( PHY_MGR_CAL_FAIL , & phy_mgr_cfg - > cal_status ) ;
/* Update the failing group/stage in the register file */
debug_info = gbl - > error_stage ;
debug_info | = gbl - > error_substage < < 8 ;
debug_info | = gbl - > error_group < < 16 ;
writel ( debug_info , & sdr_reg_file - > failing_stage ) ;
}
printf ( " %s: Calibration complete \n " , __FILE__ ) ;
}
/**
* hc_initialize_rom_data ( ) - Initialize ROM data
*
* Initialize ROM data .
*/
static void hc_initialize_rom_data ( void )
{
unsigned int nelem = 0 ;
const u32 * rom_init ;
u32 i , addr ;
socfpga_get_seq_inst_init ( & rom_init , & nelem ) ;
addr = SDR_PHYGRP_RWMGRGRP_ADDRESS | RW_MGR_INST_ROM_WRITE_OFFSET ;
for ( i = 0 ; i < nelem ; i + + )
writel ( rom_init [ i ] , addr + ( i < < 2 ) ) ;
socfpga_get_seq_ac_init ( & rom_init , & nelem ) ;
addr = SDR_PHYGRP_RWMGRGRP_ADDRESS | RW_MGR_AC_ROM_WRITE_OFFSET ;
for ( i = 0 ; i < nelem ; i + + )
writel ( rom_init [ i ] , addr + ( i < < 2 ) ) ;
}
/**
* initialize_reg_file ( ) - Initialize SDR register file
*
* Initialize SDR register file .
*/
static void initialize_reg_file ( void )
{
/* Initialize the register file with the correct data */
writel ( misccfg - > reg_file_init_seq_signature , & sdr_reg_file - > signature ) ;
writel ( 0 , & sdr_reg_file - > debug_data_addr ) ;
writel ( 0 , & sdr_reg_file - > cur_stage ) ;
writel ( 0 , & sdr_reg_file - > fom ) ;
writel ( 0 , & sdr_reg_file - > failing_stage ) ;
writel ( 0 , & sdr_reg_file - > debug1 ) ;
writel ( 0 , & sdr_reg_file - > debug2 ) ;
}
/**
* initialize_hps_phy ( ) - Initialize HPS PHY
*
* Initialize HPS PHY .
*/
static void initialize_hps_phy ( void )
{
u32 reg ;
/*
* Tracking also gets configured here because it ' s in the
* same register .
*/
u32 trk_sample_count = 7500 ;
u32 trk_long_idle_sample_count = ( 10 < < 16 ) | 100 ;
/*
* Format is number of outer loops in the 16 MSB , sample
* count in 16 LSB .
*/
reg = 0 ;
reg | = SDR_CTRLGRP_PHYCTRL_PHYCTRL_0_ACDELAYEN_SET ( 2 ) ;
reg | = SDR_CTRLGRP_PHYCTRL_PHYCTRL_0_DQDELAYEN_SET ( 1 ) ;
reg | = SDR_CTRLGRP_PHYCTRL_PHYCTRL_0_DQSDELAYEN_SET ( 1 ) ;
reg | = SDR_CTRLGRP_PHYCTRL_PHYCTRL_0_DQSLOGICDELAYEN_SET ( 1 ) ;
reg | = SDR_CTRLGRP_PHYCTRL_PHYCTRL_0_RESETDELAYEN_SET ( 0 ) ;
reg | = SDR_CTRLGRP_PHYCTRL_PHYCTRL_0_LPDDRDIS_SET ( 1 ) ;
/*
* This field selects the intrinsic latency to RDATA_EN / FULL path .
* 00 - bypass , 01 - add 5 cycles , 10 - add 10 cycles , 11 - add 15 cycles .
*/
reg | = SDR_CTRLGRP_PHYCTRL_PHYCTRL_0_ADDLATSEL_SET ( 0 ) ;
reg | = SDR_CTRLGRP_PHYCTRL_PHYCTRL_0_SAMPLECOUNT_19_0_SET (
trk_sample_count ) ;
writel ( reg , & sdr_ctrl - > phy_ctrl0 ) ;
reg = 0 ;
reg | = SDR_CTRLGRP_PHYCTRL_PHYCTRL_1_SAMPLECOUNT_31_20_SET (
trk_sample_count > >
SDR_CTRLGRP_PHYCTRL_PHYCTRL_0_SAMPLECOUNT_19_0_WIDTH ) ;
reg | = SDR_CTRLGRP_PHYCTRL_PHYCTRL_1_LONGIDLESAMPLECOUNT_19_0_SET (
trk_long_idle_sample_count ) ;
writel ( reg , & sdr_ctrl - > phy_ctrl1 ) ;
reg = 0 ;
reg | = SDR_CTRLGRP_PHYCTRL_PHYCTRL_2_LONGIDLESAMPLECOUNT_31_20_SET (
trk_long_idle_sample_count > >
SDR_CTRLGRP_PHYCTRL_PHYCTRL_1_LONGIDLESAMPLECOUNT_19_0_WIDTH ) ;
writel ( reg , & sdr_ctrl - > phy_ctrl2 ) ;
}
/**
* initialize_tracking ( ) - Initialize tracking
*
* Initialize the register file with usable initial data .
*/
static void initialize_tracking ( void )
{
/*
* Initialize the register file with the correct data .
* Compute usable version of value in case we skip full
* computation later .
*/
writel ( DIV_ROUND_UP ( iocfg - > delay_per_opa_tap ,
iocfg - > delay_per_dchain_tap ) - 1 ,
& sdr_reg_file - > dtaps_per_ptap ) ;
/* trk_sample_count */
writel ( 7500 , & sdr_reg_file - > trk_sample_count ) ;
/* longidle outer loop [15:0] */
writel ( ( 10 < < 16 ) | ( 100 < < 0 ) , & sdr_reg_file - > trk_longidle ) ;
/*
* longidle sample count [ 31 : 24 ]
* trfc , worst case of 933 Mhz 4 Gb [ 23 : 16 ]
* trcd , worst case [ 15 : 8 ]
* vfifo wait [ 7 : 0 ]
*/
writel ( ( 243 < < 24 ) | ( 14 < < 16 ) | ( 10 < < 8 ) | ( 4 < < 0 ) ,
& sdr_reg_file - > delays ) ;
/* mux delay */
writel ( ( rwcfg - > idle < < 24 ) | ( rwcfg - > activate_1 < < 16 ) |
( rwcfg - > sgle_read < < 8 ) | ( rwcfg - > precharge_all < < 0 ) ,
& sdr_reg_file - > trk_rw_mgr_addr ) ;
writel ( rwcfg - > mem_if_read_dqs_width ,
& sdr_reg_file - > trk_read_dqs_width ) ;
/* trefi [7:0] */
writel ( ( rwcfg - > refresh_all < < 24 ) | ( 1000 < < 0 ) ,
& sdr_reg_file - > trk_rfsh ) ;
}
int sdram_calibration_full ( void )
{
struct param_type my_param ;
struct gbl_type my_gbl ;
u32 pass ;
memset ( & my_param , 0 , sizeof ( my_param ) ) ;
memset ( & my_gbl , 0 , sizeof ( my_gbl ) ) ;
param = & my_param ;
gbl = & my_gbl ;
rwcfg = socfpga_get_sdram_rwmgr_config ( ) ;
iocfg = socfpga_get_sdram_io_config ( ) ;
misccfg = socfpga_get_sdram_misc_config ( ) ;
/* Set the calibration enabled by default */
gbl - > phy_debug_mode_flags | = PHY_DEBUG_ENABLE_CAL_RPT ;
/*
* Only sweep all groups ( regardless of fail state ) by default
* Set enabled read test by default .
*/
# if DISABLE_GUARANTEED_READ
gbl - > phy_debug_mode_flags | = PHY_DEBUG_DISABLE_GUARANTEED_READ ;
# endif
/* Initialize the register file */
initialize_reg_file ( ) ;
/* Initialize any PHY CSR */
initialize_hps_phy ( ) ;
scc_mgr_initialize ( ) ;
initialize_tracking ( ) ;
printf ( " %s: Preparing to start memory calibration \n " , __FILE__ ) ;
debug ( " %s:%d \n " , __func__ , __LINE__ ) ;
debug_cond ( DLEVEL = = 1 ,
" DDR3 FULL_RATE ranks=%u cs/dimm=%u dq/dqs=%u,%u vg/dqs=%u,%u " ,
rwcfg - > mem_number_of_ranks , rwcfg - > mem_number_of_cs_per_dimm ,
rwcfg - > mem_dq_per_read_dqs , rwcfg - > mem_dq_per_write_dqs ,
rwcfg - > mem_virtual_groups_per_read_dqs ,
rwcfg - > mem_virtual_groups_per_write_dqs ) ;
debug_cond ( DLEVEL = = 1 ,
" dqs=%u,%u dq=%u dm=%u ptap_delay=%u dtap_delay=%u " ,
rwcfg - > mem_if_read_dqs_width , rwcfg - > mem_if_write_dqs_width ,
rwcfg - > mem_data_width , rwcfg - > mem_data_mask_width ,
iocfg - > delay_per_opa_tap , iocfg - > delay_per_dchain_tap ) ;
debug_cond ( DLEVEL = = 1 , " dtap_dqsen_delay=%u, dll=%u " ,
iocfg - > delay_per_dqs_en_dchain_tap , iocfg - > dll_chain_length ) ;
debug_cond ( DLEVEL = = 1 ,
" max values: en_p=%u dqdqs_p=%u en_d=%u dqs_in_d=%u " ,
iocfg - > dqs_en_phase_max , iocfg - > dqdqs_out_phase_max ,
iocfg - > dqs_en_delay_max , iocfg - > dqs_in_delay_max ) ;
debug_cond ( DLEVEL = = 1 , " io_in_d=%u io_out1_d=%u io_out2_d=%u " ,
iocfg - > io_in_delay_max , iocfg - > io_out1_delay_max ,
iocfg - > io_out2_delay_max ) ;
debug_cond ( DLEVEL = = 1 , " dqs_in_reserve=%u dqs_out_reserve=%u \n " ,
iocfg - > dqs_in_reserve , iocfg - > dqs_out_reserve ) ;
hc_initialize_rom_data ( ) ;
/* update info for sims */
reg_file_set_stage ( CAL_STAGE_NIL ) ;
reg_file_set_group ( 0 ) ;
/*
* Load global needed for those actions that require
* some dynamic calibration support .
*/
dyn_calib_steps = STATIC_CALIB_STEPS ;
/*
* Load global to allow dynamic selection of delay loop settings
* based on calibration mode .
*/
if ( ! ( dyn_calib_steps & CALIB_SKIP_DELAY_LOOPS ) )
skip_delay_mask = 0xff ;
else
skip_delay_mask = 0x0 ;
pass = run_mem_calibrate ( ) ;
debug_mem_calibrate ( pass ) ;
return pass ;
}