u-boot/drivers/ddr/marvell/axp/ddr3_init.c

1217 lines
29 KiB
C
Raw Normal View History

// SPDX-License-Identifier: GPL-2.0
arm: mvebu: drivers/ddr: Add DDR3 driver with training code from Marvell bin_hdr This patch adds the DDR3 setup and training code taken from the Marvell U-Boot repository. This code used to be included as a binary (bin_hdr) into the AXP boot image. Not linked with the main U-Boot. With this code addition and the following serdes/PHY setup code, the Armada-XP support in mainline U-Boot is finally self-contained. So the complete image for booting can be built from mainline U-Boot. Without any additional external inclusion. Hopefully other MVEBU SoC's will follow here. Support for some SoC's has been removed in this version. This is: MV_MSYS: The code referred to by the MV_MSYS define is currently unused. And its not really planned to support this in mainline. So lets remove it to make the code clearer and increase the readability. MV88F68XX (A38x): The code referred to by the MV88F68XX define (A38x) is currently unused. And its partial and not sufficient for this device in this stage. So lets remove it to make the code clearer and increase the readability. MV88F66XX (ALP): The code referred to by the MV88F66XX define is currently unused. And its not really planned to support this in mainline. So lets remove it to make the code clearer and increase the readability. MV88F78X60_Z1: The code referred to by the MV88F78X60_Z1 define is currently unused. As the Z1 revision of the AXP is not supported in mainline anymore. So lets remove it to make the code clearer and increase the readability. Remove support for Z1 & A0 AXP revisions (steppings). The current stepping is B0 and this is the only one that is actively supported in this code version. Tested on AXP using a SPD DIMM setup on the Marvell DB-MV784MP-GP board and on a custom fixed DDR configuration board (maxbcm). Note: This code has undergone many hours of coding-style cleanup and refactoring. It still is not checkpatch clean though, I'm afraid. As the factoring of the code has so many levels of indentation that many lines are longer than 80 chars. This might be some task to tackly later on. Signed-off-by: Stefan Roese <sr@denx.de> Reviewed-by: Luka Perkov <luka.perkov@sartura.hr>
2015-01-19 10:33:40 +00:00
/*
* Copyright (C) Marvell International Ltd. and its affiliates
*/
#include <common.h>
#include <i2c.h>
#include <log.h>
arm: mvebu: drivers/ddr: Add DDR3 driver with training code from Marvell bin_hdr This patch adds the DDR3 setup and training code taken from the Marvell U-Boot repository. This code used to be included as a binary (bin_hdr) into the AXP boot image. Not linked with the main U-Boot. With this code addition and the following serdes/PHY setup code, the Armada-XP support in mainline U-Boot is finally self-contained. So the complete image for booting can be built from mainline U-Boot. Without any additional external inclusion. Hopefully other MVEBU SoC's will follow here. Support for some SoC's has been removed in this version. This is: MV_MSYS: The code referred to by the MV_MSYS define is currently unused. And its not really planned to support this in mainline. So lets remove it to make the code clearer and increase the readability. MV88F68XX (A38x): The code referred to by the MV88F68XX define (A38x) is currently unused. And its partial and not sufficient for this device in this stage. So lets remove it to make the code clearer and increase the readability. MV88F66XX (ALP): The code referred to by the MV88F66XX define is currently unused. And its not really planned to support this in mainline. So lets remove it to make the code clearer and increase the readability. MV88F78X60_Z1: The code referred to by the MV88F78X60_Z1 define is currently unused. As the Z1 revision of the AXP is not supported in mainline anymore. So lets remove it to make the code clearer and increase the readability. Remove support for Z1 & A0 AXP revisions (steppings). The current stepping is B0 and this is the only one that is actively supported in this code version. Tested on AXP using a SPD DIMM setup on the Marvell DB-MV784MP-GP board and on a custom fixed DDR configuration board (maxbcm). Note: This code has undergone many hours of coding-style cleanup and refactoring. It still is not checkpatch clean though, I'm afraid. As the factoring of the code has so many levels of indentation that many lines are longer than 80 chars. This might be some task to tackly later on. Signed-off-by: Stefan Roese <sr@denx.de> Reviewed-by: Luka Perkov <luka.perkov@sartura.hr>
2015-01-19 10:33:40 +00:00
#include <spl.h>
#include <asm/io.h>
#include <asm/arch/cpu.h>
#include <asm/arch/soc.h>
#include <linux/delay.h>
arm: mvebu: drivers/ddr: Add DDR3 driver with training code from Marvell bin_hdr This patch adds the DDR3 setup and training code taken from the Marvell U-Boot repository. This code used to be included as a binary (bin_hdr) into the AXP boot image. Not linked with the main U-Boot. With this code addition and the following serdes/PHY setup code, the Armada-XP support in mainline U-Boot is finally self-contained. So the complete image for booting can be built from mainline U-Boot. Without any additional external inclusion. Hopefully other MVEBU SoC's will follow here. Support for some SoC's has been removed in this version. This is: MV_MSYS: The code referred to by the MV_MSYS define is currently unused. And its not really planned to support this in mainline. So lets remove it to make the code clearer and increase the readability. MV88F68XX (A38x): The code referred to by the MV88F68XX define (A38x) is currently unused. And its partial and not sufficient for this device in this stage. So lets remove it to make the code clearer and increase the readability. MV88F66XX (ALP): The code referred to by the MV88F66XX define is currently unused. And its not really planned to support this in mainline. So lets remove it to make the code clearer and increase the readability. MV88F78X60_Z1: The code referred to by the MV88F78X60_Z1 define is currently unused. As the Z1 revision of the AXP is not supported in mainline anymore. So lets remove it to make the code clearer and increase the readability. Remove support for Z1 & A0 AXP revisions (steppings). The current stepping is B0 and this is the only one that is actively supported in this code version. Tested on AXP using a SPD DIMM setup on the Marvell DB-MV784MP-GP board and on a custom fixed DDR configuration board (maxbcm). Note: This code has undergone many hours of coding-style cleanup and refactoring. It still is not checkpatch clean though, I'm afraid. As the factoring of the code has so many levels of indentation that many lines are longer than 80 chars. This might be some task to tackly later on. Signed-off-by: Stefan Roese <sr@denx.de> Reviewed-by: Luka Perkov <luka.perkov@sartura.hr>
2015-01-19 10:33:40 +00:00
#include "ddr3_init.h"
#if defined(MV88F78X60)
#include "ddr3_axp_vars.h"
#elif defined(MV88F67XX)
#include "ddr3_a370_vars.h"
#elif defined(MV88F672X)
#include "ddr3_a375_vars.h"
#endif
#ifdef STATIC_TRAINING
static void ddr3_static_training_init(void);
#endif
#ifdef DUNIT_STATIC
static void ddr3_static_mc_init(void);
#endif
#if defined(DUNIT_STATIC) || defined(STATIC_TRAINING)
MV_DRAM_MODES *ddr3_get_static_ddr_mode(void);
#endif
#if defined(MV88F672X)
void get_target_freq(u32 freq_mode, u32 *ddr_freq, u32 *hclk_ps);
#endif
u32 mv_board_id_get(void);
extern void ddr3_set_sw_wl_rl_debug(u32);
extern void ddr3_set_pbs(u32);
extern void ddr3_set_log_level(u32 val);
static u32 log_level = DDR3_LOG_LEVEL;
static u32 ddr3_init_main(void);
/*
* Name: ddr3_set_log_level
* Desc: This routine initialize the log_level acording to nLogLevel
* which getting from user
* Args: nLogLevel
* Notes:
* Returns: None.
*/
void ddr3_set_log_level(u32 val)
{
log_level = val;
}
/*
* Name: ddr3_get_log_level
* Desc: This routine returns the log level
* Args: none
* Notes:
* Returns: log level.
*/
u32 ddr3_get_log_level(void)
{
return log_level;
}
static void debug_print_reg(u32 reg)
{
printf("0x%08x = 0x%08x\n", reg, reg_read(reg));
}
static void print_dunit_setup(void)
{
puts("\n########### LOG LEVEL 1 (D-UNIT SETUP)###########\n");
#ifdef DUNIT_STATIC
puts("\nStatic D-UNIT Setup:\n");
#endif
#ifdef DUNIT_SPD
puts("\nDynamic(using SPD) D-UNIT Setup:\n");
#endif
debug_print_reg(REG_SDRAM_CONFIG_ADDR);
debug_print_reg(REG_DUNIT_CTRL_LOW_ADDR);
debug_print_reg(REG_SDRAM_TIMING_LOW_ADDR);
debug_print_reg(REG_SDRAM_TIMING_HIGH_ADDR);
debug_print_reg(REG_SDRAM_ADDRESS_CTRL_ADDR);
debug_print_reg(REG_SDRAM_OPEN_PAGES_ADDR);
debug_print_reg(REG_SDRAM_OPERATION_ADDR);
debug_print_reg(REG_SDRAM_MODE_ADDR);
debug_print_reg(REG_SDRAM_EXT_MODE_ADDR);
debug_print_reg(REG_DDR_CONT_HIGH_ADDR);
debug_print_reg(REG_ODT_TIME_LOW_ADDR);
debug_print_reg(REG_SDRAM_ERROR_ADDR);
debug_print_reg(REG_SDRAM_AUTO_PWR_SAVE_ADDR);
debug_print_reg(REG_OUDDR3_TIMING_ADDR);
debug_print_reg(REG_ODT_TIME_HIGH_ADDR);
debug_print_reg(REG_SDRAM_ODT_CTRL_LOW_ADDR);
debug_print_reg(REG_SDRAM_ODT_CTRL_HIGH_ADDR);
debug_print_reg(REG_DUNIT_ODT_CTRL_ADDR);
#ifndef MV88F67XX
debug_print_reg(REG_DRAM_FIFO_CTRL_ADDR);
debug_print_reg(REG_DRAM_AXI_CTRL_ADDR);
debug_print_reg(REG_DRAM_ADDR_CTRL_DRIVE_STRENGTH_ADDR);
debug_print_reg(REG_DRAM_DATA_DQS_DRIVE_STRENGTH_ADDR);
debug_print_reg(REG_DRAM_VER_CAL_MACHINE_CTRL_ADDR);
debug_print_reg(REG_DRAM_MAIN_PADS_CAL_ADDR);
debug_print_reg(REG_DRAM_HOR_CAL_MACHINE_CTRL_ADDR);
debug_print_reg(REG_CS_SIZE_SCRATCH_ADDR);
debug_print_reg(REG_DYNAMIC_POWER_SAVE_ADDR);
debug_print_reg(REG_READ_DATA_SAMPLE_DELAYS_ADDR);
debug_print_reg(REG_READ_DATA_READY_DELAYS_ADDR);
debug_print_reg(REG_DDR3_MR0_ADDR);
debug_print_reg(REG_DDR3_MR1_ADDR);
debug_print_reg(REG_DDR3_MR2_ADDR);
debug_print_reg(REG_DDR3_MR3_ADDR);
debug_print_reg(REG_DDR3_RANK_CTRL_ADDR);
debug_print_reg(REG_DRAM_PHY_CONFIG_ADDR);
debug_print_reg(REG_STATIC_DRAM_DLB_CONTROL);
debug_print_reg(DLB_BUS_OPTIMIZATION_WEIGHTS_REG);
debug_print_reg(DLB_AGING_REGISTER);
debug_print_reg(DLB_EVICTION_CONTROL_REG);
debug_print_reg(DLB_EVICTION_TIMERS_REGISTER_REG);
#if defined(MV88F672X)
debug_print_reg(REG_FASTPATH_WIN_CTRL_ADDR(0));
debug_print_reg(REG_FASTPATH_WIN_BASE_ADDR(0));
debug_print_reg(REG_FASTPATH_WIN_CTRL_ADDR(1));
debug_print_reg(REG_FASTPATH_WIN_BASE_ADDR(1));
#else
debug_print_reg(REG_FASTPATH_WIN_0_CTRL_ADDR);
#endif
debug_print_reg(REG_CDI_CONFIG_ADDR);
#endif
}
#if !defined(STATIC_TRAINING)
static void ddr3_restore_and_set_final_windows(u32 *win_backup)
{
u32 ui, reg, cs;
u32 win_ctrl_reg, num_of_win_regs;
u32 cs_ena = ddr3_get_cs_ena_from_reg();
#if defined(MV88F672X)
if (DDR3_FAST_PATH_EN == 0)
return;
#endif
#if defined(MV88F672X)
win_ctrl_reg = REG_XBAR_WIN_16_CTRL_ADDR;
num_of_win_regs = 8;
#else
win_ctrl_reg = REG_XBAR_WIN_4_CTRL_ADDR;
num_of_win_regs = 16;
#endif
/* Return XBAR windows 4-7 or 16-19 init configuration */
for (ui = 0; ui < num_of_win_regs; ui++)
reg_write((win_ctrl_reg + 0x4 * ui), win_backup[ui]);
DEBUG_INIT_FULL_S("DDR3 Training Sequence - Switching XBAR Window to FastPath Window\n");
#if defined(MV88F672X)
/* Set L2 filtering to 1G */
reg_write(0x8c04, 0x40000000);
/* Open fast path windows */
for (cs = 0; cs < MAX_CS; cs++) {
if (cs_ena & (1 << cs)) {
/* set fast path window control for the cs */
reg = 0x1FFFFFE1;
reg |= (cs << 2);
reg |= (SDRAM_CS_SIZE & 0xFFFF0000);
/* Open fast path Window */
reg_write(REG_FASTPATH_WIN_CTRL_ADDR(cs), reg);
/* set fast path window base address for the cs */
reg = (((SDRAM_CS_SIZE + 1) * cs) & 0xFFFF0000);
/* Set base address */
reg_write(REG_FASTPATH_WIN_BASE_ADDR(cs), reg);
}
}
#else
reg = 0x1FFFFFE1;
for (cs = 0; cs < MAX_CS; cs++) {
if (cs_ena & (1 << cs)) {
reg |= (cs << 2);
break;
}
}
/* Open fast path Window to - 0.5G */
reg_write(REG_FASTPATH_WIN_0_CTRL_ADDR, reg);
#endif
}
static void ddr3_save_and_set_training_windows(u32 *win_backup)
{
u32 cs_ena = ddr3_get_cs_ena_from_reg();
u32 reg, tmp_count, cs, ui;
u32 win_ctrl_reg, win_base_reg, win_remap_reg;
u32 num_of_win_regs, win_jump_index;
#if defined(MV88F672X)
/* Disable L2 filtering */
reg_write(0x8c04, 0);
win_ctrl_reg = REG_XBAR_WIN_16_CTRL_ADDR;
win_base_reg = REG_XBAR_WIN_16_BASE_ADDR;
win_remap_reg = REG_XBAR_WIN_16_REMAP_ADDR;
win_jump_index = 0x8;
num_of_win_regs = 8;
#else
win_ctrl_reg = REG_XBAR_WIN_4_CTRL_ADDR;
win_base_reg = REG_XBAR_WIN_4_BASE_ADDR;
win_remap_reg = REG_XBAR_WIN_4_REMAP_ADDR;
win_jump_index = 0x10;
num_of_win_regs = 16;
#endif
/* Close XBAR Window 19 - Not needed */
/* {0x000200e8} - Open Mbus Window - 2G */
reg_write(REG_XBAR_WIN_19_CTRL_ADDR, 0);
/* Save XBAR Windows 4-19 init configurations */
for (ui = 0; ui < num_of_win_regs; ui++)
win_backup[ui] = reg_read(win_ctrl_reg + 0x4 * ui);
/* Open XBAR Windows 4-7 or 16-19 for other CS */
reg = 0;
tmp_count = 0;
for (cs = 0; cs < MAX_CS; cs++) {
if (cs_ena & (1 << cs)) {
switch (cs) {
case 0:
reg = 0x0E00;
break;
case 1:
reg = 0x0D00;
break;
case 2:
reg = 0x0B00;
break;
case 3:
reg = 0x0700;
break;
}
reg |= (1 << 0);
reg |= (SDRAM_CS_SIZE & 0xFFFF0000);
reg_write(win_ctrl_reg + win_jump_index * tmp_count,
reg);
reg = ((SDRAM_CS_SIZE + 1) * (tmp_count)) & 0xFFFF0000;
reg_write(win_base_reg + win_jump_index * tmp_count,
reg);
if (win_remap_reg <= REG_XBAR_WIN_7_REMAP_ADDR) {
reg_write(win_remap_reg +
win_jump_index * tmp_count, 0);
}
tmp_count++;
}
}
}
#endif /* !defined(STATIC_TRAINING) */
/*
* Name: ddr3_init - Main DDR3 Init function
* Desc: This routine initialize the DDR3 MC and runs HW training.
* Args: None.
* Notes:
* Returns: None.
*/
int ddr3_init(void)
{
unsigned int status;
ddr3_set_pbs(DDR3_PBS);
ddr3_set_sw_wl_rl_debug(DDR3_RUN_SW_WHEN_HW_FAIL);
status = ddr3_init_main();
if (status == MV_DDR3_TRAINING_ERR_BAD_SAR)
DEBUG_INIT_S("DDR3 Training Error: Bad sample at reset");
if (status == MV_DDR3_TRAINING_ERR_BAD_DIMM_SETUP)
DEBUG_INIT_S("DDR3 Training Error: Bad DIMM setup");
if (status == MV_DDR3_TRAINING_ERR_MAX_CS_LIMIT)
DEBUG_INIT_S("DDR3 Training Error: Max CS limit");
if (status == MV_DDR3_TRAINING_ERR_MAX_ENA_CS_LIMIT)
DEBUG_INIT_S("DDR3 Training Error: Max enable CS limit");
if (status == MV_DDR3_TRAINING_ERR_BAD_R_DIMM_SETUP)
DEBUG_INIT_S("DDR3 Training Error: Bad R-DIMM setup");
if (status == MV_DDR3_TRAINING_ERR_TWSI_FAIL)
DEBUG_INIT_S("DDR3 Training Error: TWSI failure");
if (status == MV_DDR3_TRAINING_ERR_DIMM_TYPE_NO_MATCH)
DEBUG_INIT_S("DDR3 Training Error: DIMM type no match");
if (status == MV_DDR3_TRAINING_ERR_TWSI_BAD_TYPE)
DEBUG_INIT_S("DDR3 Training Error: TWSI bad type");
if (status == MV_DDR3_TRAINING_ERR_BUS_WIDTH_NOT_MATCH)
DEBUG_INIT_S("DDR3 Training Error: bus width no match");
if (status > MV_DDR3_TRAINING_ERR_HW_FAIL_BASE)
DEBUG_INIT_C("DDR3 Training Error: HW Failure 0x", status, 8);
return status;
}
static void print_ddr_target_freq(u32 cpu_freq, u32 fab_opt)
{
puts("\nDDR3 Training Sequence - Run DDR3 at ");
switch (cpu_freq) {
#if defined(MV88F672X)
case 21:
puts("533 Mhz\n");
break;
#else
case 1:
puts("533 Mhz\n");
break;
case 2:
if (fab_opt == 5)
puts("600 Mhz\n");
if (fab_opt == 9)
puts("400 Mhz\n");
break;
case 3:
puts("667 Mhz\n");
break;
case 4:
if (fab_opt == 5)
puts("750 Mhz\n");
if (fab_opt == 9)
puts("500 Mhz\n");
break;
case 0xa:
puts("400 Mhz\n");
break;
case 0xb:
if (fab_opt == 5)
puts("800 Mhz\n");
if (fab_opt == 9)
puts("553 Mhz\n");
if (fab_opt == 0xA)
puts("640 Mhz\n");
break;
#endif
default:
puts("NOT DEFINED FREQ\n");
}
}
static u32 ddr3_init_main(void)
{
u32 target_freq;
u32 reg = 0;
u32 cpu_freq, fab_opt, hclk_time_ps, soc_num;
__maybe_unused u32 ecc = DRAM_ECC;
__maybe_unused int dqs_clk_aligned = 0;
__maybe_unused u32 scrub_offs, scrub_size;
__maybe_unused u32 ddr_width = BUS_WIDTH;
__maybe_unused int status;
__maybe_unused u32 win_backup[16];
/* SoC/Board special Initializtions */
fab_opt = ddr3_get_fab_opt();
#ifdef CONFIG_SPD_EEPROM
i2c_init(CONFIG_SYS_I2C_SPEED, CONFIG_SYS_I2C_SLAVE);
#endif
ddr3_print_version();
DEBUG_INIT_S("4\n");
/* Lib version 5.5.4 */
fab_opt = ddr3_get_fab_opt();
/* Switching CPU to MRVL ID */
soc_num = (reg_read(REG_SAMPLE_RESET_HIGH_ADDR) & SAR1_CPU_CORE_MASK) >>
SAR1_CPU_CORE_OFFSET;
switch (soc_num) {
case 0x3:
reg_bit_set(CPU_CONFIGURATION_REG(3), CPU_MRVL_ID_OFFSET);
reg_bit_set(CPU_CONFIGURATION_REG(2), CPU_MRVL_ID_OFFSET);
case 0x1:
reg_bit_set(CPU_CONFIGURATION_REG(1), CPU_MRVL_ID_OFFSET);
case 0x0:
reg_bit_set(CPU_CONFIGURATION_REG(0), CPU_MRVL_ID_OFFSET);
default:
break;
}
/* Power down deskew PLL */
#if !defined(MV88F672X)
/* 0x18780 [25] */
reg = (reg_read(REG_DDRPHY_APLL_CTRL_ADDR) & ~(1 << 25));
reg_write(REG_DDRPHY_APLL_CTRL_ADDR, reg);
#endif
/*
* Stage 0 - Set board configuration
*/
cpu_freq = ddr3_get_cpu_freq();
if (fab_opt > FAB_OPT)
fab_opt = FAB_OPT - 1;
if (ddr3_get_log_level() > 0)
print_ddr_target_freq(cpu_freq, fab_opt);
#if defined(MV88F672X)
get_target_freq(cpu_freq, &target_freq, &hclk_time_ps);
#else
target_freq = cpu_ddr_ratios[fab_opt][cpu_freq];
hclk_time_ps = cpu_fab_clk_to_hclk[fab_opt][cpu_freq];
#endif
if ((target_freq == 0) || (hclk_time_ps == 0)) {
DEBUG_INIT_S("DDR3 Training Sequence - FAILED - Wrong Sample at Reset Configurations\n");
if (target_freq == 0) {
DEBUG_INIT_C("target_freq", target_freq, 2);
DEBUG_INIT_C("fab_opt", fab_opt, 2);
DEBUG_INIT_C("cpu_freq", cpu_freq, 2);
} else if (hclk_time_ps == 0) {
DEBUG_INIT_C("hclk_time_ps", hclk_time_ps, 2);
DEBUG_INIT_C("fab_opt", fab_opt, 2);
DEBUG_INIT_C("cpu_freq", cpu_freq, 2);
}
return MV_DDR3_TRAINING_ERR_BAD_SAR;
}
#if defined(ECC_SUPPORT)
scrub_offs = U_BOOT_START_ADDR;
scrub_size = U_BOOT_SCRUB_SIZE;
#else
scrub_offs = 0;
scrub_size = 0;
#endif
#if defined(ECC_SUPPORT) && defined(AUTO_DETECTION_SUPPORT)
ecc = 0;
if (ddr3_check_config(BUS_WIDTH_ECC_TWSI_ADDR, CONFIG_ECC))
ecc = 1;
#endif
#ifdef DQS_CLK_ALIGNED
dqs_clk_aligned = 1;
#endif
/* Check if DRAM is already initialized */
if (reg_read(REG_BOOTROM_ROUTINE_ADDR) &
(1 << REG_BOOTROM_ROUTINE_DRAM_INIT_OFFS)) {
DEBUG_INIT_S("DDR3 Training Sequence - 2nd boot - Skip\n");
return MV_OK;
}
/*
* Stage 1 - Dunit Setup
*/
#ifdef DUNIT_STATIC
/*
* For Static D-Unit Setup use must set the correct static values
* at the ddr3_*soc*_vars.h file
*/
DEBUG_INIT_FULL_S("DDR3 Training Sequence - Static MC Init\n");
ddr3_static_mc_init();
#ifdef ECC_SUPPORT
ecc = DRAM_ECC;
if (ecc) {
reg = reg_read(REG_SDRAM_CONFIG_ADDR);
reg |= (1 << REG_SDRAM_CONFIG_ECC_OFFS);
reg_write(REG_SDRAM_CONFIG_ADDR, reg);
}
#endif
#endif
#if defined(MV88F78X60) || defined(MV88F672X)
#if defined(AUTO_DETECTION_SUPPORT)
/*
* Configurations for both static and dynamic MC setups
*
* Dynamically Set 32Bit and ECC for AXP (Relevant only for
* Marvell DB boards)
*/
if (ddr3_check_config(BUS_WIDTH_ECC_TWSI_ADDR, CONFIG_BUS_WIDTH)) {
ddr_width = 32;
DEBUG_INIT_S("DDR3 Training Sequence - DRAM bus width 32Bit\n");
}
#endif
#if defined(MV88F672X)
reg = reg_read(REG_SDRAM_CONFIG_ADDR);
if ((reg >> 15) & 1)
ddr_width = 32;
else
ddr_width = 16;
#endif
#endif
#ifdef DUNIT_SPD
status = ddr3_dunit_setup(ecc, hclk_time_ps, &ddr_width);
if (MV_OK != status) {
DEBUG_INIT_S("DDR3 Training Sequence - FAILED (ddr3 Dunit Setup)\n");
return status;
}
#endif
/* Fix read ready phases for all SOC in reg 0x15C8 */
reg = reg_read(REG_TRAINING_DEBUG_3_ADDR);
reg &= ~(REG_TRAINING_DEBUG_3_MASK);
reg |= 0x4; /* Phase 0 */
reg &= ~(REG_TRAINING_DEBUG_3_MASK << REG_TRAINING_DEBUG_3_OFFS);
reg |= (0x4 << (1 * REG_TRAINING_DEBUG_3_OFFS)); /* Phase 1 */
reg &= ~(REG_TRAINING_DEBUG_3_MASK << (3 * REG_TRAINING_DEBUG_3_OFFS));
reg |= (0x6 << (3 * REG_TRAINING_DEBUG_3_OFFS)); /* Phase 3 */
reg &= ~(REG_TRAINING_DEBUG_3_MASK << (4 * REG_TRAINING_DEBUG_3_OFFS));
reg |= (0x6 << (4 * REG_TRAINING_DEBUG_3_OFFS));
reg &= ~(REG_TRAINING_DEBUG_3_MASK << (5 * REG_TRAINING_DEBUG_3_OFFS));
reg |= (0x6 << (5 * REG_TRAINING_DEBUG_3_OFFS));
reg_write(REG_TRAINING_DEBUG_3_ADDR, reg);
#if defined(MV88F672X)
/*
* AxiBrespMode[8] = Compliant,
* AxiAddrDecodeCntrl[11] = Internal,
* AxiDataBusWidth[0] = 128bit
*/
/* 0x14A8 - AXI Control Register */
reg_write(REG_DRAM_AXI_CTRL_ADDR, 0);
#else
/* 0x14A8 - AXI Control Register */
reg_write(REG_DRAM_AXI_CTRL_ADDR, 0x00000100);
reg_write(REG_CDI_CONFIG_ADDR, 0x00000006);
if ((ddr_width == 64) && (reg_read(REG_DDR_IO_ADDR) &
(1 << REG_DDR_IO_CLK_RATIO_OFFS))) {
/* 0x14A8 - AXI Control Register */
reg_write(REG_DRAM_AXI_CTRL_ADDR, 0x00000101);
reg_write(REG_CDI_CONFIG_ADDR, 0x00000007);
}
#endif
#if !defined(MV88F67XX)
/*
* ARMADA-370 activate DLB later at the u-boot,
* Armada38x - No DLB activation at this time
*/
reg_write(DLB_BUS_OPTIMIZATION_WEIGHTS_REG, 0x18C01E);
#if defined(MV88F78X60)
/* WA according to eratta GL-8672902*/
if (mv_ctrl_rev_get() == MV_78XX0_B0_REV)
reg_write(DLB_BUS_OPTIMIZATION_WEIGHTS_REG, 0xc19e);
#endif
reg_write(DLB_AGING_REGISTER, 0x0f7f007f);
reg_write(DLB_EVICTION_CONTROL_REG, 0x0);
reg_write(DLB_EVICTION_TIMERS_REGISTER_REG, 0x00FF3C1F);
reg_write(MBUS_UNITS_PRIORITY_CONTROL_REG, 0x55555555);
reg_write(FABRIC_UNITS_PRIORITY_CONTROL_REG, 0xAA);
reg_write(MBUS_UNITS_PREFETCH_CONTROL_REG, 0xffff);
reg_write(FABRIC_UNITS_PREFETCH_CONTROL_REG, 0xf0f);
#if defined(MV88F78X60)
/* WA according to eratta GL-8672902 */
if (mv_ctrl_rev_get() == MV_78XX0_B0_REV) {
reg = reg_read(REG_STATIC_DRAM_DLB_CONTROL);
reg |= DLB_ENABLE;
reg_write(REG_STATIC_DRAM_DLB_CONTROL, reg);
}
#endif /* end defined(MV88F78X60) */
#endif /* end !defined(MV88F67XX) */
if (ddr3_get_log_level() >= MV_LOG_LEVEL_1)
print_dunit_setup();
/*
* Stage 2 - Training Values Setup
*/
#ifdef STATIC_TRAINING
/*
* DRAM Init - After all the D-unit values are set, its time to init
* the D-unit
*/
/* Wait for '0' */
reg_write(REG_SDRAM_INIT_CTRL_ADDR, 0x1);
do {
reg = (reg_read(REG_SDRAM_INIT_CTRL_ADDR)) &
(1 << REG_SDRAM_INIT_CTRL_OFFS);
} while (reg);
/* ddr3 init using static parameters - HW training is disabled */
DEBUG_INIT_FULL_S("DDR3 Training Sequence - Static Training Parameters\n");
ddr3_static_training_init();
#if defined(MV88F78X60)
/*
* If ECC is enabled, need to scrub the U-Boot area memory region -
* Run training function with Xor bypass just to scrub the memory
*/
status = ddr3_hw_training(target_freq, ddr_width,
1, scrub_offs, scrub_size,
dqs_clk_aligned, DDR3_TRAINING_DEBUG,
REG_DIMM_SKIP_WL);
if (MV_OK != status) {
DEBUG_INIT_FULL_S("DDR3 Training Sequence - FAILED\n");
return status;
}
#endif
#else
/* Set X-BAR windows for the training sequence */
ddr3_save_and_set_training_windows(win_backup);
/* Run DDR3 Training Sequence */
/* DRAM Init */
reg_write(REG_SDRAM_INIT_CTRL_ADDR, 0x1);
do {
reg = (reg_read(REG_SDRAM_INIT_CTRL_ADDR)) &
(1 << REG_SDRAM_INIT_CTRL_OFFS);
} while (reg); /* Wait for '0' */
/* ddr3 init using DDR3 HW training procedure */
DEBUG_INIT_FULL_S("DDR3 Training Sequence - HW Training Procedure\n");
status = ddr3_hw_training(target_freq, ddr_width,
0, scrub_offs, scrub_size,
dqs_clk_aligned, DDR3_TRAINING_DEBUG,
REG_DIMM_SKIP_WL);
if (MV_OK != status) {
DEBUG_INIT_FULL_S("DDR3 Training Sequence - FAILED\n");
return status;
}
#endif
/*
* Stage 3 - Finish
*/
#if defined(MV88F78X60) || defined(MV88F672X)
/* Disable ECC Ignore bit */
reg = reg_read(REG_SDRAM_CONFIG_ADDR) &
~(1 << REG_SDRAM_CONFIG_IERR_OFFS);
reg_write(REG_SDRAM_CONFIG_ADDR, reg);
#endif
#if !defined(STATIC_TRAINING)
/* Restore and set windows */
ddr3_restore_and_set_final_windows(win_backup);
#endif
/* Update DRAM init indication in bootROM register */
reg = reg_read(REG_BOOTROM_ROUTINE_ADDR);
reg_write(REG_BOOTROM_ROUTINE_ADDR,
reg | (1 << REG_BOOTROM_ROUTINE_DRAM_INIT_OFFS));
#if !defined(MV88F67XX)
#if defined(MV88F78X60)
if (mv_ctrl_rev_get() == MV_78XX0_B0_REV) {
reg = reg_read(REG_SDRAM_CONFIG_ADDR);
if (ecc == 0)
reg_write(REG_SDRAM_CONFIG_ADDR, reg | (1 << 19));
}
#endif /* end defined(MV88F78X60) */
reg_write(DLB_EVICTION_CONTROL_REG, 0x9);
reg = reg_read(REG_STATIC_DRAM_DLB_CONTROL);
reg |= (DLB_ENABLE | DLB_WRITE_COALESING | DLB_AXI_PREFETCH_EN |
DLB_MBUS_PREFETCH_EN | PREFETCH_NLNSZTR);
reg_write(REG_STATIC_DRAM_DLB_CONTROL, reg);
#endif /* end !defined(MV88F67XX) */
#ifdef STATIC_TRAINING
DEBUG_INIT_S("DDR3 Training Sequence - Ended Successfully (S)\n");
#else
DEBUG_INIT_S("DDR3 Training Sequence - Ended Successfully\n");
#endif
return MV_OK;
}
/*
* Name: ddr3_get_cpu_freq
* Desc: read S@R and return CPU frequency
* Args:
* Notes:
* Returns: required value
*/
u32 ddr3_get_cpu_freq(void)
{
u32 reg, cpu_freq;
#if defined(MV88F672X)
/* Read sample at reset setting */
reg = reg_read(REG_SAMPLE_RESET_HIGH_ADDR); /* 0xE8200 */
cpu_freq = (reg & REG_SAMPLE_RESET_CPU_FREQ_MASK) >>
REG_SAMPLE_RESET_CPU_FREQ_OFFS;
#else
/* Read sample at reset setting */
reg = reg_read(REG_SAMPLE_RESET_LOW_ADDR); /* 0x18230 [23:21] */
#if defined(MV88F78X60)
cpu_freq = (reg & REG_SAMPLE_RESET_CPU_FREQ_MASK) >>
REG_SAMPLE_RESET_CPU_FREQ_OFFS;
reg = reg_read(REG_SAMPLE_RESET_HIGH_ADDR); /* 0x18234 [20] */
cpu_freq |= (((reg >> REG_SAMPLE_RESET_HIGH_CPU_FREQ_OFFS) & 0x1) << 3);
#elif defined(MV88F67XX)
cpu_freq = (reg & REG_SAMPLE_RESET_CPU_FREQ_MASK) >>
REG_SAMPLE_RESET_CPU_FREQ_OFFS;
#endif
#endif
return cpu_freq;
}
/*
* Name: ddr3_get_fab_opt
* Desc: read S@R and return CPU frequency
* Args:
* Notes:
* Returns: required value
*/
u32 ddr3_get_fab_opt(void)
{
__maybe_unused u32 reg, fab_opt;
#if defined(MV88F672X)
return 0; /* No fabric */
#else
/* Read sample at reset setting */
reg = reg_read(REG_SAMPLE_RESET_LOW_ADDR);
fab_opt = (reg & REG_SAMPLE_RESET_FAB_MASK) >>
REG_SAMPLE_RESET_FAB_OFFS;
#if defined(MV88F78X60)
reg = reg_read(REG_SAMPLE_RESET_HIGH_ADDR);
fab_opt |= (((reg >> 19) & 0x1) << 4);
#endif
return fab_opt;
#endif
}
/*
* Name: ddr3_get_vco_freq
* Desc: read S@R and return VCO frequency
* Args:
* Notes:
* Returns: required value
*/
u32 ddr3_get_vco_freq(void)
{
u32 fab, cpu_freq, ui_vco_freq;
fab = ddr3_get_fab_opt();
cpu_freq = ddr3_get_cpu_freq();
if (fab == 2 || fab == 3 || fab == 7 || fab == 8 || fab == 10 ||
fab == 15 || fab == 17 || fab == 20)
ui_vco_freq = cpu_freq + CLK_CPU;
else
ui_vco_freq = cpu_freq;
return ui_vco_freq;
}
#ifdef STATIC_TRAINING
/*
* Name: ddr3_static_training_init - Init DDR3 Training with
* static parameters
* Desc: Use this routine to init the controller without the HW training
* procedure
* User must provide compatible header file with registers data.
* Args: None.
* Notes:
* Returns: None.
*/
void ddr3_static_training_init(void)
{
MV_DRAM_MODES *ddr_mode;
u32 reg;
int j;
ddr_mode = ddr3_get_static_ddr_mode();
j = 0;
while (ddr_mode->vals[j].reg_addr != 0) {
udelay(10); /* haim want to delay each write */
reg_write(ddr_mode->vals[j].reg_addr,
ddr_mode->vals[j].reg_value);
if (ddr_mode->vals[j].reg_addr ==
REG_PHY_REGISTRY_FILE_ACCESS_ADDR)
do {
reg = reg_read(REG_PHY_REGISTRY_FILE_ACCESS_ADDR) &
REG_PHY_REGISTRY_FILE_ACCESS_OP_DONE;
} while (reg);
j++;
}
}
#endif
/*
* Name: ddr3_get_static_mc_value - Init Memory controller with static
* parameters
* Desc: Use this routine to init the controller without the HW training
* procedure
* User must provide compatible header file with registers data.
* Args: None.
* Notes:
* Returns: None.
*/
u32 ddr3_get_static_mc_value(u32 reg_addr, u32 offset1, u32 mask1, u32 offset2,
u32 mask2)
{
u32 reg, tmp;
reg = reg_read(reg_addr);
tmp = (reg >> offset1) & mask1;
if (mask2)
tmp |= (reg >> offset2) & mask2;
return tmp;
}
/*
* Name: ddr3_get_static_ddr_mode - Init Memory controller with static
* parameters
* Desc: Use this routine to init the controller without the HW training
* procedure
* User must provide compatible header file with registers data.
* Args: None.
* Notes:
* Returns: None.
*/
__weak MV_DRAM_MODES *ddr3_get_static_ddr_mode(void)
{
u32 chip_board_rev, i;
u32 size;
/* Do not modify this code. relevant only for marvell Boards */
#if defined(DB_78X60_PCAC)
chip_board_rev = Z1_PCAC;
#elif defined(DB_78X60_AMC)
chip_board_rev = A0_AMC;
#elif defined(DB_88F6710_PCAC)
chip_board_rev = A0_PCAC;
#elif defined(RD_88F6710)
chip_board_rev = A0_RD;
#elif defined(MV88F672X)
chip_board_rev = mv_board_id_get();
#else
chip_board_rev = A0;
#endif
size = sizeof(ddr_modes) / sizeof(MV_DRAM_MODES);
for (i = 0; i < size; i++) {
if ((ddr3_get_cpu_freq() == ddr_modes[i].cpu_freq) &&
(ddr3_get_fab_opt() == ddr_modes[i].fab_freq) &&
(chip_board_rev == ddr_modes[i].chip_board_rev))
return &ddr_modes[i];
}
return &ddr_modes[0];
}
#ifdef DUNIT_STATIC
/*
* Name: ddr3_static_mc_init - Init Memory controller with static parameters
* Desc: Use this routine to init the controller without the HW training
* procedure
* User must provide compatible header file with registers data.
* Args: None.
* Notes:
* Returns: None.
*/
void ddr3_static_mc_init(void)
{
MV_DRAM_MODES *ddr_mode;
u32 reg;
int j;
ddr_mode = ddr3_get_static_ddr_mode();
j = 0;
while (ddr_mode->regs[j].reg_addr != 0) {
reg_write(ddr_mode->regs[j].reg_addr,
ddr_mode->regs[j].reg_value);
if (ddr_mode->regs[j].reg_addr ==
REG_PHY_REGISTRY_FILE_ACCESS_ADDR)
do {
reg = reg_read(REG_PHY_REGISTRY_FILE_ACCESS_ADDR) &
REG_PHY_REGISTRY_FILE_ACCESS_OP_DONE;
} while (reg);
j++;
}
}
#endif
/*
* Name: ddr3_check_config - Check user configurations: ECC/MultiCS
* Desc:
* Args: twsi Address
* Notes: Only Available for ArmadaXP/Armada 370 DB boards
* Returns: None.
*/
int ddr3_check_config(u32 twsi_addr, MV_CONFIG_TYPE config_type)
{
#ifdef AUTO_DETECTION_SUPPORT
u8 data = 0;
int ret;
int offset;
if ((config_type == CONFIG_ECC) || (config_type == CONFIG_BUS_WIDTH))
offset = 1;
else
offset = 0;
ret = i2c_read(twsi_addr, offset, 1, (u8 *)&data, 1);
if (!ret) {
switch (config_type) {
case CONFIG_ECC:
if (data & 0x2)
return 1;
break;
case CONFIG_BUS_WIDTH:
if (data & 0x1)
return 1;
break;
#ifdef DB_88F6710
case CONFIG_MULTI_CS:
if (CFG_MULTI_CS_MODE(data))
return 1;
break;
#else
case CONFIG_MULTI_CS:
break;
#endif
}
}
#endif
return 0;
}
#if defined(DB_88F78X60_REV2)
/*
* Name: ddr3_get_eprom_fabric - Get Fabric configuration from EPROM
* Desc:
* Args: twsi Address
* Notes: Only Available for ArmadaXP DB Rev2 boards
* Returns: None.
*/
u8 ddr3_get_eprom_fabric(void)
{
#ifdef AUTO_DETECTION_SUPPORT
u8 data = 0;
int ret;
ret = i2c_read(NEW_FABRIC_TWSI_ADDR, 1, 1, (u8 *)&data, 1);
if (!ret)
return data & 0x1F;
#endif
return 0;
}
#endif
/*
* Name: ddr3_cl_to_valid_cl - this return register matching CL value
* Desc:
* Args: clValue - the value
* Notes:
* Returns: required CL value
*/
u32 ddr3_cl_to_valid_cl(u32 cl)
{
switch (cl) {
case 5:
return 2;
break;
case 6:
return 4;
break;
case 7:
return 6;
break;
case 8:
return 8;
break;
case 9:
return 10;
break;
case 10:
return 12;
break;
case 11:
return 14;
break;
case 12:
return 1;
break;
case 13:
return 3;
break;
case 14:
return 5;
break;
default:
return 2;
}
}
/*
* Name: ddr3_cl_to_valid_cl - this return register matching CL value
* Desc:
* Args: clValue - the value
* Notes:
* Returns: required CL value
*/
u32 ddr3_valid_cl_to_cl(u32 ui_valid_cl)
{
switch (ui_valid_cl) {
case 1:
return 12;
break;
case 2:
return 5;
break;
case 3:
return 13;
break;
case 4:
return 6;
break;
case 5:
return 14;
break;
case 6:
return 7;
break;
case 8:
return 8;
break;
case 10:
return 9;
break;
case 12:
return 10;
break;
case 14:
return 11;
break;
default:
return 0;
}
}
/*
* Name: ddr3_get_cs_num_from_reg
* Desc:
* Args:
* Notes:
* Returns:
*/
u32 ddr3_get_cs_num_from_reg(void)
{
u32 cs_ena = ddr3_get_cs_ena_from_reg();
u32 cs_count = 0;
u32 cs;
for (cs = 0; cs < MAX_CS; cs++) {
if (cs_ena & (1 << cs))
cs_count++;
}
return cs_count;
}
/*
* Name: ddr3_get_cs_ena_from_reg
* Desc:
* Args:
* Notes:
* Returns:
*/
u32 ddr3_get_cs_ena_from_reg(void)
{
return reg_read(REG_DDR3_RANK_CTRL_ADDR) &
REG_DDR3_RANK_CTRL_CS_ENA_MASK;
}
/*
* mv_ctrl_rev_get - Get Marvell controller device revision number
*
* DESCRIPTION:
* This function returns 8bit describing the device revision as defined
* in PCI Express Class Code and Revision ID Register.
*
* INPUT:
* None.
*
* OUTPUT:
* None.
*
* RETURN:
* 8bit desscribing Marvell controller revision number
*
*/
#if !defined(MV88F672X)
u8 mv_ctrl_rev_get(void)
{
u8 rev_num;
#if defined(MV_INCLUDE_CLK_PWR_CNTRL)
/* Check pex power state */
u32 pex_power;
pex_power = mv_ctrl_pwr_clck_get(PEX_UNIT_ID, 0);
if (pex_power == 0)
mv_ctrl_pwr_clck_set(PEX_UNIT_ID, 0, 1);
#endif
rev_num = (u8)reg_read(PEX_CFG_DIRECT_ACCESS(0,
PCI_CLASS_CODE_AND_REVISION_ID));
#if defined(MV_INCLUDE_CLK_PWR_CNTRL)
/* Return to power off state */
if (pex_power == 0)
mv_ctrl_pwr_clck_set(PEX_UNIT_ID, 0, 0);
#endif
return (rev_num & PCCRIR_REVID_MASK) >> PCCRIR_REVID_OFFS;
}
#endif
#if defined(MV88F672X)
void get_target_freq(u32 freq_mode, u32 *ddr_freq, u32 *hclk_ps)
{
u32 tmp, hclk;
switch (freq_mode) {
case CPU_333MHz_DDR_167MHz_L2_167MHz:
hclk = 84;
tmp = DDR_100;
break;
case CPU_266MHz_DDR_266MHz_L2_133MHz:
case CPU_333MHz_DDR_222MHz_L2_167MHz:
case CPU_400MHz_DDR_200MHz_L2_200MHz:
case CPU_400MHz_DDR_267MHz_L2_200MHz:
case CPU_533MHz_DDR_267MHz_L2_267MHz:
case CPU_500MHz_DDR_250MHz_L2_250MHz:
case CPU_600MHz_DDR_300MHz_L2_300MHz:
case CPU_800MHz_DDR_267MHz_L2_400MHz:
case CPU_900MHz_DDR_300MHz_L2_450MHz:
tmp = DDR_300;
hclk = 150;
break;
case CPU_333MHz_DDR_333MHz_L2_167MHz:
case CPU_500MHz_DDR_334MHz_L2_250MHz:
case CPU_666MHz_DDR_333MHz_L2_333MHz:
tmp = DDR_333;
hclk = 165;
break;
case CPU_533MHz_DDR_356MHz_L2_267MHz:
tmp = DDR_360;
hclk = 180;
break;
case CPU_400MHz_DDR_400MHz_L2_200MHz:
case CPU_600MHz_DDR_400MHz_L2_300MHz:
case CPU_800MHz_DDR_400MHz_L2_400MHz:
case CPU_400MHz_DDR_400MHz_L2_400MHz:
tmp = DDR_400;
hclk = 200;
break;
case CPU_666MHz_DDR_444MHz_L2_333MHz:
case CPU_900MHz_DDR_450MHz_L2_450MHz:
tmp = DDR_444;
hclk = 222;
break;
case CPU_500MHz_DDR_500MHz_L2_250MHz:
case CPU_1000MHz_DDR_500MHz_L2_500MHz:
case CPU_1000MHz_DDR_500MHz_L2_333MHz:
tmp = DDR_500;
hclk = 250;
break;
case CPU_533MHz_DDR_533MHz_L2_267MHz:
case CPU_800MHz_DDR_534MHz_L2_400MHz:
case CPU_1100MHz_DDR_550MHz_L2_550MHz:
tmp = DDR_533;
hclk = 267;
break;
case CPU_600MHz_DDR_600MHz_L2_300MHz:
case CPU_900MHz_DDR_600MHz_L2_450MHz:
case CPU_1200MHz_DDR_600MHz_L2_600MHz:
tmp = DDR_600;
hclk = 300;
break;
case CPU_666MHz_DDR_666MHz_L2_333MHz:
case CPU_1000MHz_DDR_667MHz_L2_500MHz:
tmp = DDR_666;
hclk = 333;
break;
default:
*ddr_freq = 0;
*hclk_ps = 0;
break;
}
*ddr_freq = tmp; /* DDR freq define */
*hclk_ps = 1000000 / hclk; /* values are 1/HCLK in ps */
return;
}
#endif