diff options
author | Stefan Roese <sr@denx.de> | 2015-01-19 11:33:40 +0100 |
---|---|---|
committer | Luka Perkov <luka.perkov@sartura.hr> | 2015-02-06 17:25:03 +0100 |
commit | f8d25d7466142c1d4ebf879e2a83a9cfc19559cb (patch) | |
tree | a825659225817a9adf8c74d0ac604278fa7a0df4 /drivers/ddr | |
parent | 2e19cc316f38c56e3bbc127491bc8722c1b0386c (diff) | |
download | u-boot-imx-f8d25d7466142c1d4ebf879e2a83a9cfc19559cb.zip u-boot-imx-f8d25d7466142c1d4ebf879e2a83a9cfc19559cb.tar.gz u-boot-imx-f8d25d7466142c1d4ebf879e2a83a9cfc19559cb.tar.bz2 |
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>
Diffstat (limited to 'drivers/ddr')
-rw-r--r-- | drivers/ddr/mvebu/Makefile | 14 | ||||
-rw-r--r-- | drivers/ddr/mvebu/ddr3_axp.h | 510 | ||||
-rw-r--r-- | drivers/ddr/mvebu/ddr3_axp_config.h | 146 | ||||
-rw-r--r-- | drivers/ddr/mvebu/ddr3_axp_mc_static.h | 284 | ||||
-rw-r--r-- | drivers/ddr/mvebu/ddr3_axp_training_static.h | 770 | ||||
-rw-r--r-- | drivers/ddr/mvebu/ddr3_axp_vars.h | 226 | ||||
-rw-r--r-- | drivers/ddr/mvebu/ddr3_dfs.c | 1552 | ||||
-rw-r--r-- | drivers/ddr/mvebu/ddr3_dqs.c | 1374 | ||||
-rw-r--r-- | drivers/ddr/mvebu/ddr3_hw_training.c | 1115 | ||||
-rw-r--r-- | drivers/ddr/mvebu/ddr3_hw_training.h | 392 | ||||
-rw-r--r-- | drivers/ddr/mvebu/ddr3_init.c | 1219 | ||||
-rw-r--r-- | drivers/ddr/mvebu/ddr3_init.h | 143 | ||||
-rw-r--r-- | drivers/ddr/mvebu/ddr3_patterns_64bit.h | 924 | ||||
-rw-r--r-- | drivers/ddr/mvebu/ddr3_pbs.c | 1592 | ||||
-rw-r--r-- | drivers/ddr/mvebu/ddr3_read_leveling.c | 1214 | ||||
-rw-r--r-- | drivers/ddr/mvebu/ddr3_sdram.c | 669 | ||||
-rw-r--r-- | drivers/ddr/mvebu/ddr3_spd.c | 1300 | ||||
-rw-r--r-- | drivers/ddr/mvebu/ddr3_write_leveling.c | 1366 | ||||
-rw-r--r-- | drivers/ddr/mvebu/xor.c | 436 | ||||
-rw-r--r-- | drivers/ddr/mvebu/xor.h | 70 | ||||
-rw-r--r-- | drivers/ddr/mvebu/xor_regs.h | 103 |
21 files changed, 15419 insertions, 0 deletions
diff --git a/drivers/ddr/mvebu/Makefile b/drivers/ddr/mvebu/Makefile new file mode 100644 index 0000000..50a69ea --- /dev/null +++ b/drivers/ddr/mvebu/Makefile @@ -0,0 +1,14 @@ +# +# SPDX-License-Identifier: GPL-2.0+ +# + +obj-$(CONFIG_SPL_BUILD) += ddr3_dfs.o +obj-$(CONFIG_SPL_BUILD) += ddr3_dqs.o +obj-$(CONFIG_SPL_BUILD) += ddr3_hw_training.o +obj-$(CONFIG_SPL_BUILD) += ddr3_init.o +obj-$(CONFIG_SPL_BUILD) += ddr3_pbs.o +obj-$(CONFIG_SPL_BUILD) += ddr3_read_leveling.o +obj-$(CONFIG_SPL_BUILD) += ddr3_sdram.o +obj-$(CONFIG_SPL_BUILD) += ddr3_spd.o +obj-$(CONFIG_SPL_BUILD) += ddr3_write_leveling.o +obj-$(CONFIG_SPL_BUILD) += xor.o diff --git a/drivers/ddr/mvebu/ddr3_axp.h b/drivers/ddr/mvebu/ddr3_axp.h new file mode 100644 index 0000000..bf65f6b --- /dev/null +++ b/drivers/ddr/mvebu/ddr3_axp.h @@ -0,0 +1,510 @@ +/* + * Copyright (C) Marvell International Ltd. and its affiliates + * + * SPDX-License-Identifier: GPL-2.0 + */ + +#ifndef __DDR3_AXP_H +#define __DDR3_AXP_H + +#define MV_78XX0_Z1_REV 0x0 +#define MV_78XX0_A0_REV 0x1 +#define MV_78XX0_B0_REV 0x2 + +#define SAR_DDR3_FREQ_MASK 0xFE00000 +#define SAR_CPU_FAB_GET(cpu, fab) (((cpu & 0x7) << 21) | ((fab & 0xF) << 24)) + +#define MAX_CS 4 + +#define MIN_DIMM_ADDR 0x50 +#define FAR_END_DIMM_ADDR 0x50 +#define MAX_DIMM_ADDR 0x60 + +#ifndef CONFIG_DDR_FIXED_SIZE +#define SDRAM_CS_SIZE 0xFFFFFFF +#else +#define SDRAM_CS_SIZE (CONFIG_DDR_FIXED_SIZE - 1) +#endif +#define SDRAM_CS_BASE 0x0 +#define SDRAM_DIMM_SIZE 0x80000000 + +#define CPU_CONFIGURATION_REG(id) (0x21800 + (id * 0x100)) +#define CPU_MRVL_ID_OFFSET 0x10 +#define SAR1_CPU_CORE_MASK 0x00000018 +#define SAR1_CPU_CORE_OFFSET 3 + +#define ECC_SUPPORT +#define NEW_FABRIC_TWSI_ADDR 0x4E +#ifdef DB_784MP_GP +#define BUS_WIDTH_ECC_TWSI_ADDR 0x4E +#else +#define BUS_WIDTH_ECC_TWSI_ADDR 0x4F +#endif +#define MV_MAX_DDR3_STATIC_SIZE 50 +#define MV_DDR3_MODES_NUMBER 30 + +#define RESUME_RL_PATTERNS_ADDR (0xFE0000) +#define RESUME_RL_PATTERNS_SIZE (0x100) +#define RESUME_TRAINING_VALUES_ADDR (RESUME_RL_PATTERNS_ADDR + RESUME_RL_PATTERNS_SIZE) +#define RESUME_TRAINING_VALUES_MAX (0xCD0) +#define BOOT_INFO_ADDR (RESUME_RL_PATTERNS_ADDR + 0x1000) +#define CHECKSUM_RESULT_ADDR (BOOT_INFO_ADDR + 0x1000) +#define NUM_OF_REGISTER_ADDR (CHECKSUM_RESULT_ADDR + 4) +#define SUSPEND_MAGIC_WORD (0xDEADB002) +#define REGISTER_LIST_END (0xFFFFFFFF) + +/* + * Registers offset + */ + +#define REG_SAMPLE_RESET_LOW_ADDR 0x18230 +#define REG_SAMPLE_RESET_HIGH_ADDR 0x18234 +#define REG_SAMPLE_RESET_CPU_FREQ_OFFS 21 +#define REG_SAMPLE_RESET_CPU_FREQ_MASK 0x00E00000 +#define REG_SAMPLE_RESET_FAB_OFFS 24 +#define REG_SAMPLE_RESET_FAB_MASK 0xF000000 +#define REG_SAMPLE_RESET_TCLK_OFFS 28 +#define REG_SAMPLE_RESET_CPU_ARCH_OFFS 31 +#define REG_SAMPLE_RESET_HIGH_CPU_FREQ_OFFS 20 + +/* MISC */ +/* + * In mainline U-Boot we're re-configuring the mvebu base address + * register to 0xf1000000. So need to use this value for the DDR + * training code as well. + */ +#define INTER_REGS_BASE SOC_REGS_PHY_BASE + +/* DDR */ +#define REG_SDRAM_CONFIG_ADDR 0x1400 +#define REG_SDRAM_CONFIG_MASK 0x9FFFFFFF +#define REG_SDRAM_CONFIG_RFRS_MASK 0x3FFF +#define REG_SDRAM_CONFIG_WIDTH_OFFS 15 +#define REG_SDRAM_CONFIG_REGDIMM_OFFS 17 +#define REG_SDRAM_CONFIG_ECC_OFFS 18 +#define REG_SDRAM_CONFIG_IERR_OFFS 19 +#define REG_SDRAM_CONFIG_PUPRSTDIV_OFFS 28 +#define REG_SDRAM_CONFIG_RSTRD_OFFS 30 + +#define REG_DUNIT_CTRL_LOW_ADDR 0x1404 +#define REG_DUNIT_CTRL_LOW_2T_OFFS 3 +#define REG_DUNIT_CTRL_LOW_2T_MASK 0x3 +#define REG_DUNIT_CTRL_LOW_DPDE_OFFS 14 + +#define REG_SDRAM_TIMING_LOW_ADDR 0x1408 + +#define REG_SDRAM_TIMING_HIGH_ADDR 0x140C +#define REG_SDRAM_TIMING_H_R2R_OFFS 7 +#define REG_SDRAM_TIMING_H_R2R_MASK 0x3 +#define REG_SDRAM_TIMING_H_R2W_W2R_OFFS 9 +#define REG_SDRAM_TIMING_H_R2W_W2R_MASK 0x3 +#define REG_SDRAM_TIMING_H_W2W_OFFS 11 +#define REG_SDRAM_TIMING_H_W2W_MASK 0x1F +#define REG_SDRAM_TIMING_H_R2R_H_OFFS 19 +#define REG_SDRAM_TIMING_H_R2R_H_MASK 0x7 +#define REG_SDRAM_TIMING_H_R2W_W2R_H_OFFS 22 +#define REG_SDRAM_TIMING_H_R2W_W2R_H_MASK 0x7 + +#define REG_SDRAM_ADDRESS_CTRL_ADDR 0x1410 +#define REG_SDRAM_ADDRESS_SIZE_OFFS 2 +#define REG_SDRAM_ADDRESS_SIZE_HIGH_OFFS 18 +#define REG_SDRAM_ADDRESS_CTRL_STRUCT_OFFS 4 + +#define REG_SDRAM_OPEN_PAGES_ADDR 0x1414 +#define REG_SDRAM_OPERATION_CS_OFFS 8 + +#define REG_SDRAM_OPERATION_ADDR 0x1418 +#define REG_SDRAM_OPERATION_CWA_DELAY_SEL_OFFS 24 +#define REG_SDRAM_OPERATION_CWA_DATA_OFFS 20 +#define REG_SDRAM_OPERATION_CWA_DATA_MASK 0xF +#define REG_SDRAM_OPERATION_CWA_RC_OFFS 16 +#define REG_SDRAM_OPERATION_CWA_RC_MASK 0xF +#define REG_SDRAM_OPERATION_CMD_MR0 0xF03 +#define REG_SDRAM_OPERATION_CMD_MR1 0xF04 +#define REG_SDRAM_OPERATION_CMD_MR2 0xF08 +#define REG_SDRAM_OPERATION_CMD_MR3 0xF09 +#define REG_SDRAM_OPERATION_CMD_RFRS 0xF02 +#define REG_SDRAM_OPERATION_CMD_CWA 0xF0E +#define REG_SDRAM_OPERATION_CMD_RFRS_DONE 0xF +#define REG_SDRAM_OPERATION_CMD_MASK 0xF +#define REG_SDRAM_OPERATION_CS_OFFS 8 + +#define REG_OUDDR3_TIMING_ADDR 0x142C + +#define REG_SDRAM_MODE_ADDR 0x141C + +#define REG_SDRAM_EXT_MODE_ADDR 0x1420 + +#define REG_DDR_CONT_HIGH_ADDR 0x1424 + +#define REG_ODT_TIME_LOW_ADDR 0x1428 +#define REG_ODT_ON_CTL_RD_OFFS 12 +#define REG_ODT_OFF_CTL_RD_OFFS 16 +#define REG_SDRAM_ERROR_ADDR 0x1454 +#define REG_SDRAM_AUTO_PWR_SAVE_ADDR 0x1474 +#define REG_ODT_TIME_HIGH_ADDR 0x147C + +#define REG_SDRAM_INIT_CTRL_ADDR 0x1480 +#define REG_SDRAM_INIT_CTRL_OFFS 0 +#define REG_SDRAM_INIT_CKE_ASSERT_OFFS 2 +#define REG_SDRAM_INIT_RESET_DEASSERT_OFFS 3 + +#define REG_SDRAM_ODT_CTRL_LOW_ADDR 0x1494 + +#define REG_SDRAM_ODT_CTRL_HIGH_ADDR 0x1498 +/*#define REG_SDRAM_ODT_CTRL_HIGH_OVRD_MASK 0xFFFFFF55 */ +#define REG_SDRAM_ODT_CTRL_HIGH_OVRD_MASK 0x0 +#define REG_SDRAM_ODT_CTRL_HIGH_OVRD_ENA 0x3 + +#define REG_DUNIT_ODT_CTRL_ADDR 0x149C +#define REG_DUNIT_ODT_CTRL_OVRD_OFFS 8 +#define REG_DUNIT_ODT_CTRL_OVRD_VAL_OFFS 9 + +#define REG_DRAM_FIFO_CTRL_ADDR 0x14A0 + +#define REG_DRAM_AXI_CTRL_ADDR 0x14A8 +#define REG_DRAM_AXI_CTRL_AXIDATABUSWIDTH_OFFS 0 + +#define REG_METAL_MASK_ADDR 0x14B0 +#define REG_METAL_MASK_MASK 0xDFFFFFFF +#define REG_METAL_MASK_RETRY_OFFS 0 + +#define REG_DRAM_ADDR_CTRL_DRIVE_STRENGTH_ADDR 0x14C0 + +#define REG_DRAM_DATA_DQS_DRIVE_STRENGTH_ADDR 0x14C4 +#define REG_DRAM_VER_CAL_MACHINE_CTRL_ADDR 0x14c8 +#define REG_DRAM_MAIN_PADS_CAL_ADDR 0x14CC + +#define REG_DRAM_HOR_CAL_MACHINE_CTRL_ADDR 0x17c8 + +#define REG_CS_SIZE_SCRATCH_ADDR 0x1504 +#define REG_DYNAMIC_POWER_SAVE_ADDR 0x1520 +#define REG_DDR_IO_ADDR 0x1524 +#define REG_DDR_IO_CLK_RATIO_OFFS 15 + +#define REG_DFS_ADDR 0x1528 +#define REG_DFS_DLLNEXTSTATE_OFFS 0 +#define REG_DFS_BLOCK_OFFS 1 +#define REG_DFS_SR_OFFS 2 +#define REG_DFS_ATSR_OFFS 3 +#define REG_DFS_RECONF_OFFS 4 +#define REG_DFS_CL_NEXT_STATE_OFFS 8 +#define REG_DFS_CL_NEXT_STATE_MASK 0xF +#define REG_DFS_CWL_NEXT_STATE_OFFS 12 +#define REG_DFS_CWL_NEXT_STATE_MASK 0x7 + +#define REG_READ_DATA_SAMPLE_DELAYS_ADDR 0x1538 +#define REG_READ_DATA_SAMPLE_DELAYS_MASK 0x1F +#define REG_READ_DATA_SAMPLE_DELAYS_OFFS 8 + +#define REG_READ_DATA_READY_DELAYS_ADDR 0x153C +#define REG_READ_DATA_READY_DELAYS_MASK 0x1F +#define REG_READ_DATA_READY_DELAYS_OFFS 8 + +#define START_BURST_IN_ADDR 1 + +#define REG_DRAM_TRAINING_SHADOW_ADDR 0x18488 +#define REG_DRAM_TRAINING_ADDR 0x15B0 +#define REG_DRAM_TRAINING_LOW_FREQ_OFFS 0 +#define REG_DRAM_TRAINING_PATTERNS_OFFS 4 +#define REG_DRAM_TRAINING_MED_FREQ_OFFS 2 +#define REG_DRAM_TRAINING_WL_OFFS 3 +#define REG_DRAM_TRAINING_RL_OFFS 6 +#define REG_DRAM_TRAINING_DQS_RX_OFFS 15 +#define REG_DRAM_TRAINING_DQS_TX_OFFS 16 +#define REG_DRAM_TRAINING_CS_OFFS 20 +#define REG_DRAM_TRAINING_RETEST_OFFS 24 +#define REG_DRAM_TRAINING_DFS_FREQ_OFFS 27 +#define REG_DRAM_TRAINING_DFS_REQ_OFFS 29 +#define REG_DRAM_TRAINING_ERROR_OFFS 30 +#define REG_DRAM_TRAINING_AUTO_OFFS 31 +#define REG_DRAM_TRAINING_RETEST_PAR 0x3 +#define REG_DRAM_TRAINING_RETEST_MASK 0xF8FFFFFF +#define REG_DRAM_TRAINING_CS_MASK 0xFF0FFFFF +#define REG_DRAM_TRAINING_PATTERNS_MASK 0xFF0F0000 + +#define REG_DRAM_TRAINING_1_ADDR 0x15B4 +#define REG_DRAM_TRAINING_1_TRNBPOINT_OFFS 16 + +#define REG_DRAM_TRAINING_2_ADDR 0x15B8 +#define REG_DRAM_TRAINING_2_OVERRUN_OFFS 17 +#define REG_DRAM_TRAINING_2_FIFO_RST_OFFS 4 +#define REG_DRAM_TRAINING_2_RL_MODE_OFFS 3 +#define REG_DRAM_TRAINING_2_WL_MODE_OFFS 2 +#define REG_DRAM_TRAINING_2_ECC_MUX_OFFS 1 +#define REG_DRAM_TRAINING_2_SW_OVRD_OFFS 0 + +#define REG_DRAM_TRAINING_PATTERN_BASE_ADDR 0x15BC +#define REG_DRAM_TRAINING_PATTERN_BASE_OFFS 3 + +#define REG_TRAINING_DEBUG_2_ADDR 0x15C4 +#define REG_TRAINING_DEBUG_2_OFFS 16 +#define REG_TRAINING_DEBUG_2_MASK 0x3 + +#define REG_TRAINING_DEBUG_3_ADDR 0x15C8 +#define REG_TRAINING_DEBUG_3_OFFS 3 +#define REG_TRAINING_DEBUG_3_MASK 0x7 + +#define MR_CS_ADDR_OFFS 4 + +#define REG_DDR3_MR0_ADDR 0x15D0 +#define REG_DDR3_MR0_CS_ADDR 0x1870 +#define REG_DDR3_MR0_CL_MASK 0x74 +#define REG_DDR3_MR0_CL_OFFS 2 +#define REG_DDR3_MR0_CL_HIGH_OFFS 3 +#define CL_MASK 0xF + +#define REG_DDR3_MR1_ADDR 0x15D4 +#define REG_DDR3_MR1_CS_ADDR 0x1874 +#define REG_DDR3_MR1_RTT_MASK 0xFFFFFDBB +#define REG_DDR3_MR1_DLL_ENA_OFFS 0 +#define REG_DDR3_MR1_RTT_DISABLED 0x0 +#define REG_DDR3_MR1_RTT_RZQ2 0x40 +#define REG_DDR3_MR1_RTT_RZQ4 0x2 +#define REG_DDR3_MR1_RTT_RZQ6 0x42 +#define REG_DDR3_MR1_RTT_RZQ8 0x202 +#define REG_DDR3_MR1_RTT_RZQ12 0x4 +#define REG_DDR3_MR1_OUTBUF_WL_MASK 0xFFFFEF7F /* WL-disabled,OB-enabled */ +#define REG_DDR3_MR1_OUTBUF_DIS_OFFS 12 /* Output Buffer Disabled */ +#define REG_DDR3_MR1_WL_ENA_OFFS 7 +#define REG_DDR3_MR1_WL_ENA 0x80 /* WL Enabled */ +#define REG_DDR3_MR1_ODT_MASK 0xFFFFFDBB + +#define REG_DDR3_MR2_ADDR 0x15D8 +#define REG_DDR3_MR2_CS_ADDR 0x1878 +#define REG_DDR3_MR2_CWL_OFFS 3 +#define REG_DDR3_MR2_CWL_MASK 0x7 +#define REG_DDR3_MR2_ODT_MASK 0xFFFFF9FF +#define REG_DDR3_MR3_ADDR 0x15DC +#define REG_DDR3_MR3_CS_ADDR 0x187C + +#define REG_DDR3_RANK_CTRL_ADDR 0x15E0 +#define REG_DDR3_RANK_CTRL_CS_ENA_MASK 0xF +#define REG_DDR3_RANK_CTRL_MIRROR_OFFS 4 + +#define REG_ZQC_CONF_ADDR 0x15E4 + +#define REG_DRAM_PHY_CONFIG_ADDR 0x15EC +#define REG_DRAM_PHY_CONFIG_MASK 0x3FFFFFFF + +#define REG_ODPG_CNTRL_ADDR 0x1600 +#define REG_ODPG_CNTRL_OFFS 21 + +#define REG_PHY_LOCK_MASK_ADDR 0x1670 +#define REG_PHY_LOCK_MASK_MASK 0xFFFFF000 + +#define REG_PHY_LOCK_STATUS_ADDR 0x1674 +#define REG_PHY_LOCK_STATUS_LOCK_OFFS 9 +#define REG_PHY_LOCK_STATUS_LOCK_MASK 0xFFF +#define REG_PHY_LOCK_APLL_ADLL_STATUS_MASK 0x7FF + +#define REG_PHY_REGISTRY_FILE_ACCESS_ADDR 0x16A0 +#define REG_PHY_REGISTRY_FILE_ACCESS_OP_WR 0xC0000000 +#define REG_PHY_REGISTRY_FILE_ACCESS_OP_RD 0x80000000 +#define REG_PHY_REGISTRY_FILE_ACCESS_OP_DONE 0x80000000 +#define REG_PHY_BC_OFFS 27 +#define REG_PHY_CNTRL_OFFS 26 +#define REG_PHY_CS_OFFS 16 +#define REG_PHY_DQS_REF_DLY_OFFS 10 +#define REG_PHY_PHASE_OFFS 8 +#define REG_PHY_PUP_OFFS 22 + +#define REG_TRAINING_WL_ADDR 0x16AC +#define REG_TRAINING_WL_CS_MASK 0xFFFFFFFC +#define REG_TRAINING_WL_UPD_OFFS 2 +#define REG_TRAINING_WL_CS_DONE_OFFS 3 +#define REG_TRAINING_WL_RATIO_MASK 0xFFFFFF0F +#define REG_TRAINING_WL_1TO1 0x50 +#define REG_TRAINING_WL_2TO1 0x10 +#define REG_TRAINING_WL_DELAYEXP_MASK 0x20000000 +#define REG_TRAINING_WL_RESULTS_MASK 0x000001FF +#define REG_TRAINING_WL_RESULTS_OFFS 20 + +#define REG_REGISTERED_DRAM_CTRL_ADDR 0x16D0 +#define REG_REGISTERED_DRAM_CTRL_SR_FLOAT_OFFS 15 +#define REG_REGISTERED_DRAM_CTRL_PARITY_MASK 0x3F +/* DLB*/ +#define REG_STATIC_DRAM_DLB_CONTROL 0x1700 +#define DLB_BUS_OPTIMIZATION_WEIGHTS_REG 0x1704 +#define DLB_AGING_REGISTER 0x1708 +#define DLB_EVICTION_CONTROL_REG 0x170c +#define DLB_EVICTION_TIMERS_REGISTER_REG 0x1710 + +#define DLB_ENABLE 0x1 +#define DLB_WRITE_COALESING (0x1 << 2) +#define DLB_AXI_PREFETCH_EN (0x1 << 3) +#define DLB_MBUS_PREFETCH_EN (0x1 << 4) +#define PREFETCH_NLNSZTR (0x1 << 6) + +/* CPU */ +#define REG_BOOTROM_ROUTINE_ADDR 0x182D0 +#define REG_BOOTROM_ROUTINE_DRAM_INIT_OFFS 12 + +#define REG_DRAM_INIT_CTRL_STATUS_ADDR 0x18488 +#define REG_DRAM_INIT_CTRL_TRN_CLK_OFFS 16 +#define REG_CPU_DIV_CLK_CTRL_0_NEW_RATIO 0x000200FF +#define REG_DRAM_INIT_CTRL_STATUS_2_ADDR 0x1488 + +#define REG_CPU_DIV_CLK_CTRL_0_ADDR 0x18700 + +#define REG_CPU_DIV_CLK_CTRL_1_ADDR 0x18704 +#define REG_CPU_DIV_CLK_CTRL_2_ADDR 0x18708 + +#define REG_CPU_DIV_CLK_CTRL_3_ADDR 0x1870C +#define REG_CPU_DIV_CLK_CTRL_3_FREQ_MASK 0xFFFFC0FF +#define REG_CPU_DIV_CLK_CTRL_3_FREQ_OFFS 8 + +#define REG_CPU_DIV_CLK_CTRL_4_ADDR 0x18710 + +#define REG_CPU_DIV_CLK_STATUS_0_ADDR 0x18718 +#define REG_CPU_DIV_CLK_ALL_STABLE_OFFS 8 + +#define REG_CPU_PLL_CTRL_0_ADDR 0x1871C +#define REG_CPU_PLL_STATUS_0_ADDR 0x18724 +#define REG_CORE_DIV_CLK_CTRL_ADDR 0x18740 +#define REG_CORE_DIV_CLK_STATUS_ADDR 0x18744 +#define REG_DDRPHY_APLL_CTRL_ADDR 0x18780 + +#define REG_DDRPHY_APLL_CTRL_2_ADDR 0x18784 + +#define REG_SFABRIC_CLK_CTRL_ADDR 0x20858 +#define REG_SFABRIC_CLK_CTRL_SMPL_OFFS 8 + +/* DRAM Windows */ +#define REG_XBAR_WIN_19_CTRL_ADDR 0x200e8 +#define REG_XBAR_WIN_4_CTRL_ADDR 0x20040 +#define REG_XBAR_WIN_4_BASE_ADDR 0x20044 +#define REG_XBAR_WIN_4_REMAP_ADDR 0x20048 +#define REG_FASTPATH_WIN_0_CTRL_ADDR 0x20184 +#define REG_XBAR_WIN_7_REMAP_ADDR 0x20078 + +/* SRAM */ +#define REG_CDI_CONFIG_ADDR 0x20220 +#define REG_SRAM_WINDOW_0_ADDR 0x20240 +#define REG_SRAM_WINDOW_0_ENA_OFFS 0 +#define REG_SRAM_WINDOW_1_ADDR 0x20244 +#define REG_SRAM_L2_ENA_ADDR 0x8500 +#define REG_SRAM_CLEAN_BY_WAY_ADDR 0x87BC + +/* PMU */ +#define REG_PMU_I_F_CTRL_ADDR 0x1C090 +#define REG_PMU_DUNIT_BLK_OFFS 16 +#define REG_PMU_DUNIT_RFRS_OFFS 20 +#define REG_PMU_DUNIT_ACK_OFFS 24 + +/* MBUS*/ +#define MBUS_UNITS_PRIORITY_CONTROL_REG (MV_MBUS_REGS_OFFSET + 0x420) +#define FABRIC_UNITS_PRIORITY_CONTROL_REG (MV_MBUS_REGS_OFFSET + 0x424) +#define MBUS_UNITS_PREFETCH_CONTROL_REG (MV_MBUS_REGS_OFFSET + 0x428) +#define FABRIC_UNITS_PREFETCH_CONTROL_REG (MV_MBUS_REGS_OFFSET + 0x42c) + +#define REG_PM_STAT_MASK_ADDR 0x2210C +#define REG_PM_STAT_MASK_CPU0_IDLE_MASK_OFFS 16 + +#define REG_PM_EVENT_STAT_MASK_ADDR 0x22120 +#define REG_PM_EVENT_STAT_MASK_DFS_DONE_OFFS 17 + +#define REG_PM_CTRL_CONFIG_ADDR 0x22104 +#define REG_PM_CTRL_CONFIG_DFS_REQ_OFFS 18 + +#define REG_FABRIC_LOCAL_IRQ_MASK_ADDR 0x218C4 +#define REG_FABRIC_LOCAL_IRQ_PMU_MASK_OFFS 18 + +/* Controller revision info */ +#define PCI_CLASS_CODE_AND_REVISION_ID 0x008 +#define PCCRIR_REVID_OFFS 0 /* Revision ID */ +#define PCCRIR_REVID_MASK (0xff << PCCRIR_REVID_OFFS) + +/* Power Management Clock Gating Control Register */ +#define MV_PEX_IF_REGS_OFFSET(if) \ + (if < 8 ? (0x40000 + ((if) / 4) * 0x40000 + ((if) % 4) * 0x4000) \ + : (0x42000 + ((if) % 8) * 0x40000)) +#define MV_PEX_IF_REGS_BASE(unit) (MV_PEX_IF_REGS_OFFSET(unit)) +#define POWER_MNG_CTRL_REG 0x18220 +#define PEX_DEVICE_AND_VENDOR_ID 0x000 +#define PEX_CFG_DIRECT_ACCESS(if, reg) (MV_PEX_IF_REGS_BASE(if) + (reg)) +#define PMC_PEXSTOPCLOCK_OFFS(port) ((port) < 8 ? (5 + (port)) : (18 + (port))) +#define PMC_PEXSTOPCLOCK_MASK(port) (1 << PMC_PEXSTOPCLOCK_OFFS(port)) +#define PMC_PEXSTOPCLOCK_EN(port) (1 << PMC_PEXSTOPCLOCK_OFFS(port)) +#define PMC_PEXSTOPCLOCK_STOP(port) (0 << PMC_PEXSTOPCLOCK_OFFS(port)) + +/* TWSI */ +#define TWSI_DATA_ADDR_MASK 0x7 +#define TWSI_DATA_ADDR_OFFS 1 + +/* General */ +#define MAX_CS 4 + +/* Frequencies */ +#define FAB_OPT 21 +#define CLK_CPU 12 +#define CLK_VCO (2 * CLK_CPU) +#define CLK_DDR 12 + +/* Cpu Frequencies: */ +#define CLK_CPU_1000 0 +#define CLK_CPU_1066 1 +#define CLK_CPU_1200 2 +#define CLK_CPU_1333 3 +#define CLK_CPU_1500 4 +#define CLK_CPU_1666 5 +#define CLK_CPU_1800 6 +#define CLK_CPU_2000 7 +#define CLK_CPU_600 8 +#define CLK_CPU_667 9 +#define CLK_CPU_800 0xa + +/* Extra Cpu Frequencies: */ +#define CLK_CPU_1600 11 +#define CLK_CPU_2133 12 +#define CLK_CPU_2200 13 +#define CLK_CPU_2400 14 + +/* DDR3 Frequencies: */ +#define DDR_100 0 +#define DDR_300 1 +#define DDR_333 1 +#define DDR_360 2 +#define DDR_400 3 +#define DDR_444 4 +#define DDR_500 5 +#define DDR_533 6 +#define DDR_600 7 +#define DDR_640 8 +#define DDR_666 8 +#define DDR_720 9 +#define DDR_750 9 +#define DDR_800 10 +#define DDR_833 11 +#define DDR_HCLK 20 +#define DDR_S 12 +#define DDR_S_1TO1 13 +#define MARGIN_FREQ DDR_400 +#define DFS_MARGIN DDR_100 + +#define ODT_OPT 16 +#define ODT20 0x200 +#define ODT30 0x204 +#define ODT40 0x44 +#define ODT120 0x40 +#define ODT120D 0x400 + +#define MRS_DELAY 100 + +#define SDRAM_WL_SW_OFFS 0x100 +#define SDRAM_RL_OFFS 0x0 +#define SDRAM_PBS_I_OFFS 0x140 +#define SDRAM_PBS_II_OFFS 0x180 +#define SDRAM_PBS_NEXT_OFFS (SDRAM_PBS_II_OFFS - SDRAM_PBS_I_OFFS) +#define SDRAM_PBS_TX_OFFS 0x180 +#define SDRAM_PBS_TX_DM_OFFS 576 +#define SDRAM_DQS_RX_OFFS 1024 +#define SDRAM_DQS_TX_OFFS 2048 +#define SDRAM_DQS_RX_SPECIAL_OFFS 5120 + +#define LEN_STD_PATTERN 16 +#define LEN_KILLER_PATTERN 128 +#define LEN_SPECIAL_PATTERN 128 +#define LEN_PBS_PATTERN 16 + +#endif /* __DDR3_AXP_H */ diff --git a/drivers/ddr/mvebu/ddr3_axp_config.h b/drivers/ddr/mvebu/ddr3_axp_config.h new file mode 100644 index 0000000..800d2d1 --- /dev/null +++ b/drivers/ddr/mvebu/ddr3_axp_config.h @@ -0,0 +1,146 @@ +/* + * Copyright (C) Marvell International Ltd. and its affiliates + * + * SPDX-License-Identifier: GPL-2.0 + */ + +#ifndef __DDR3_AXP_CONFIG_H +#define __DDR3_AXP_CONFIG_H + +/* + * DDR3_LOG_LEVEL Information + * + * Level 0: Provides an error code in a case of failure, RL, WL errors + * and other algorithm failure + * Level 1: Provides the D-Unit setup (SPD/Static configuration) + * Level 2: Provides the windows margin as a results of DQS centeralization + * Level 3: Provides the windows margin of each DQ as a results of DQS + * centeralization + */ +#ifdef CONFIG_DDR_LOG_LEVEL +#define DDR3_LOG_LEVEL CONFIG_DDR_LOG_LEVEL +#else +#define DDR3_LOG_LEVEL 0 +#endif + +#define DDR3_PBS 1 + +/* This flag allows the execution of SW WL/RL upon HW failure */ +#define DDR3_RUN_SW_WHEN_HW_FAIL 1 + +/* + * General Configurations + * + * The following parameters are required for proper setup: + * + * DDR_TARGET_FABRIC - Set desired fabric configuration + * (for sample@Reset fabfreq parameter) + * DRAM_ECC - Set ECC support 1/0 + * BUS_WIDTH - 64/32 bit + * CONFIG_SPD_EEPROM - Enables auto detection of DIMMs and their timing values + * DQS_CLK_ALIGNED - Set this if CLK and DQS signals are aligned on board + * MIXED_DIMM_STATIC - Mixed DIMM + On board devices support (ODT registers + * values are taken statically) + * DDR3_TRAINING_DEBUG - Debug prints of internal code + */ +#define DDR_TARGET_FABRIC 5 +#define DRAM_ECC 0 + +#ifdef MV_DDR_32BIT +#define BUS_WIDTH 32 +#else +#define BUS_WIDTH 64 +#endif + +#undef DQS_CLK_ALIGNED +#undef MIXED_DIMM_STATIC +#define DDR3_TRAINING_DEBUG 0 +#define REG_DIMM_SKIP_WL 0 + +/* Marvell boards specific configurations */ +#if defined(DB_78X60_PCAC) +#undef CONFIG_SPD_EEPROM +#define STATIC_TRAINING +#endif + +#if defined(DB_78X60_AMC) +#undef CONFIG_SPD_EEPROM +#undef DRAM_ECC +#define DRAM_ECC 1 +#endif + +#ifdef CONFIG_SPD_EEPROM +/* + * DIMM support parameters: + * DRAM_2T - Set Desired 2T Mode - 0 - 1T, 0x1 - 2T, 0x2 - 3T + * DIMM_CS_BITMAP - bitmap representing the optional CS in DIMMs + * (0xF=CS0+CS1+CS2+CS3, 0xC=CS2+CS3...) + */ +#define DRAM_2T 0x0 +#define DIMM_CS_BITMAP 0xF +#define DUNIT_SPD +#endif + +#ifdef DRAM_ECC +/* + * ECC support parameters: + * + * U_BOOT_START_ADDR, U_BOOT_SCRUB_SIZE - relevant when using ECC and need + * to configure the scrubbing area + */ +#define TRAINING_SIZE 0x20000 +#define U_BOOT_START_ADDR 0 +#define U_BOOT_SCRUB_SIZE 0x1000000 /* TRAINING_SIZE */ +#endif + +/* + * Registered DIMM Support - In case registered DIMM is attached, + * please supply the following values: + * (see JEDEC - JESD82-29A "Definition of the SSTE32882 Registering Clock + * Driver with Parity and Quad Chip + * Selects for DDR3/DDR3L/DDR3U RDIMM 1.5 V/1.35 V/1.25 V Applications") + * RC0: Global Features Control Word + * RC1: Clock Driver Enable Control Word + * RC2: Timing Control Word + * RC3-RC5 - taken from SPD + * RC8: Additional IBT Setting Control Word + * RC9: Power Saving Settings Control Word + * RC10: Encoding for RDIMM Operating Speed + * RC11: Operating Voltage VDD and VREFCA Control Word + */ +#define RDIMM_RC0 0 +#define RDIMM_RC1 0 +#define RDIMM_RC2 0 +#define RDIMM_RC8 0 +#define RDIMM_RC9 0 +#define RDIMM_RC10 0x2 +#define RDIMM_RC11 0x0 + +#if defined(MIXED_DIMM_STATIC) || !defined(CONFIG_SPD_EEPROM) +#define DUNIT_STATIC +#endif + +#if defined(MIXED_DIMM_STATIC) || defined(CONFIG_SPD_EEPROM) +/* + * This flag allows the user to change the dram refresh cycle in ps, + * only in case of SPD or MIX DIMM topology + */ +#define TREFI_USER_EN + +#ifdef TREFI_USER_EN +#define TREFI_USER 3900000 +#endif +#endif + +#ifdef CONFIG_SPD_EEPROM +/* + * AUTO_DETECTION_SUPPORT - relevant ONLY for Marvell DB boards. + * Enables I2C auto detection different options + */ +#if defined(CONFIG_DB_88F78X60) || defined(CONFIG_DB_88F78X60_REV2) || \ + defined(CONFIG_DB_784MP_GP) +#define AUTO_DETECTION_SUPPORT +#endif +#endif + +#endif /* __DDR3_AXP_CONFIG_H */ diff --git a/drivers/ddr/mvebu/ddr3_axp_mc_static.h b/drivers/ddr/mvebu/ddr3_axp_mc_static.h new file mode 100644 index 0000000..2c0e9075 --- /dev/null +++ b/drivers/ddr/mvebu/ddr3_axp_mc_static.h @@ -0,0 +1,284 @@ +/* + * Copyright (C) Marvell International Ltd. and its affiliates + * + * SPDX-License-Identifier: GPL-2.0 + */ + +#ifndef __AXP_MC_STATIC_H +#define __AXP_MC_STATIC_H + +MV_DRAM_MC_INIT ddr3_A0_db_667[MV_MAX_DDR3_STATIC_SIZE] = { +#ifdef MV_DDR_32BIT + {0x00001400, 0x7301c924}, /*DDR SDRAM Configuration Register */ +#else /*MV_DDR_64BIT */ + {0x00001400, 0x7301CA28}, /*DDR SDRAM Configuration Register */ +#endif + {0x00001404, 0x3630b800}, /*Dunit Control Low Register */ + {0x00001408, 0x43149775}, /*DDR SDRAM Timing (Low) Register */ + /* {0x0000140C, 0x38000C6A}, *//*DDR SDRAM Timing (High) Register */ + {0x0000140C, 0x38d83fe0}, /*DDR SDRAM Timing (High) Register */ + +#ifdef DB_78X60_PCAC + {0x00001410, 0x040F0001}, /*DDR SDRAM Address Control Register */ +#else + {0x00001410, 0x040F0000}, /*DDR SDRAM Open Pages Control Register */ +#endif + + {0x00001414, 0x00000000}, /*DDR SDRAM Open Pages Control Register */ + {0x00001418, 0x00000e00}, /*DDR SDRAM Operation Register */ + {0x00001420, 0x00000004}, /*DDR SDRAM Extended Mode Register */ + {0x00001424, 0x0000D3FF}, /*Dunit Control High Register */ + {0x00001428, 0x000F8830}, /*Dunit Control High Register */ + {0x0000142C, 0x214C2F38}, /*Dunit Control High Register */ + {0x0000147C, 0x0000c671}, + + {0x000014a0, 0x000002A9}, + {0x000014a8, 0x00000101}, /*2:1 */ + {0x00020220, 0x00000007}, + + {0x00001494, 0x00010000}, /*DDR SDRAM ODT Control (Low) Register */ + {0x00001498, 0x00000000}, /*DDR SDRAM ODT Control (High) Register */ + {0x0000149C, 0x00000301}, /*DDR Dunit ODT Control Register */ + + {0x000014C0, 0x192434e9}, /* DRAM address and Control Driving Strenght */ + {0x000014C4, 0x092434e9}, /* DRAM Data and DQS Driving Strenght */ + + {0x000200e8, 0x3FFF0E01}, /* DO NOT Modify - Open Mbus Window - 2G - Mbus is required for the training sequence */ + {0x00020184, 0x3FFFFFE0}, /* DO NOT Modify - Close fast path Window to - 2G */ + + {0x0001504, 0x7FFFFFF1}, /* CS0 Size */ + {0x000150C, 0x00000000}, /* CS1 Size */ + {0x0001514, 0x00000000}, /* CS2 Size */ + {0x000151C, 0x00000000}, /* CS3 Size */ + + /* {0x00001524, 0x0000C800}, */ + {0x00001538, 0x0000000b}, /*Read Data Sample Delays Register */ + {0x0000153C, 0x0000000d}, /*Read Data Ready Delay Register */ + + {0x000015D0, 0x00000640}, /*MR0 */ + {0x000015D4, 0x00000046}, /*MR1 */ + {0x000015D8, 0x00000010}, /*MR2 */ + {0x000015DC, 0x00000000}, /*MR3 */ + + {0x000015E4, 0x00203c18}, /*ZQC Configuration Register */ + {0x000015EC, 0xd800aa25}, /*DDR PHY */ + {0x0, 0x0} +}; + +MV_DRAM_MC_INIT ddr3_A0_AMC_667[MV_MAX_DDR3_STATIC_SIZE] = { +#ifdef MV_DDR_32BIT + {0x00001400, 0x7301c924}, /*DDR SDRAM Configuration Register */ +#else /*MV_DDR_64BIT */ + {0x00001400, 0x7301CA28}, /*DDR SDRAM Configuration Register */ +#endif + {0x00001404, 0x3630b800}, /*Dunit Control Low Register */ + {0x00001408, 0x43149775}, /*DDR SDRAM Timing (Low) Register */ + /* {0x0000140C, 0x38000C6A}, *//*DDR SDRAM Timing (High) Register */ + {0x0000140C, 0x38d83fe0}, /*DDR SDRAM Timing (High) Register */ + +#ifdef DB_78X60_PCAC + {0x00001410, 0x040F0001}, /*DDR SDRAM Address Control Register */ +#else + {0x00001410, 0x040F000C}, /*DDR SDRAM Open Pages Control Register */ +#endif + + {0x00001414, 0x00000000}, /*DDR SDRAM Open Pages Control Register */ + {0x00001418, 0x00000e00}, /*DDR SDRAM Operation Register */ + {0x00001420, 0x00000004}, /*DDR SDRAM Extended Mode Register */ + {0x00001424, 0x0000D3FF}, /*Dunit Control High Register */ + {0x00001428, 0x000F8830}, /*Dunit Control High Register */ + {0x0000142C, 0x214C2F38}, /*Dunit Control High Register */ + {0x0000147C, 0x0000c671}, + + {0x000014a0, 0x000002A9}, + {0x000014a8, 0x00000101}, /*2:1 */ + {0x00020220, 0x00000007}, + + {0x00001494, 0x00010000}, /*DDR SDRAM ODT Control (Low) Register */ + {0x00001498, 0x00000000}, /*DDR SDRAM ODT Control (High) Register */ + {0x0000149C, 0x00000301}, /*DDR Dunit ODT Control Register */ + + {0x000014C0, 0x192434e9}, /* DRAM address and Control Driving Strenght */ + {0x000014C4, 0x092434e9}, /* DRAM Data and DQS Driving Strenght */ + + {0x000200e8, 0x3FFF0E01}, /* DO NOT Modify - Open Mbus Window - 2G - Mbus is required for the training sequence */ + {0x00020184, 0x3FFFFFE0}, /* DO NOT Modify - Close fast path Window to - 2G */ + + {0x0001504, 0x3FFFFFF1}, /* CS0 Size */ + {0x000150C, 0x00000000}, /* CS1 Size */ + {0x0001514, 0x00000000}, /* CS2 Size */ + {0x000151C, 0x00000000}, /* CS3 Size */ + + /* {0x00001524, 0x0000C800}, */ + {0x00001538, 0x0000000b}, /*Read Data Sample Delays Register */ + {0x0000153C, 0x0000000d}, /*Read Data Ready Delay Register */ + + {0x000015D0, 0x00000640}, /*MR0 */ + {0x000015D4, 0x00000046}, /*MR1 */ + {0x000015D8, 0x00000010}, /*MR2 */ + {0x000015DC, 0x00000000}, /*MR3 */ + + {0x000015E4, 0x00203c18}, /*ZQC Configuration Register */ + {0x000015EC, 0xd800aa25}, /*DDR PHY */ + {0x0, 0x0} +}; + +MV_DRAM_MC_INIT ddr3_A0_db_400[MV_MAX_DDR3_STATIC_SIZE] = { +#ifdef MV_DDR_32BIT + {0x00001400, 0x73004C30}, /*DDR SDRAM Configuration Register */ +#else /* MV_DDR_64BIT */ + {0x00001400, 0x7300CC30}, /*DDR SDRAM Configuration Register */ +#endif + {0x00001404, 0x3630B840}, /*Dunit Control Low Register */ + {0x00001408, 0x33137663}, /*DDR SDRAM Timing (Low) Register */ + {0x0000140C, 0x38000C55}, /*DDR SDRAM Timing (High) Register */ + {0x00001410, 0x040F0000}, /*DDR SDRAM Address Control Register */ + {0x00001414, 0x00000000}, /*DDR SDRAM Open Pages Control Register */ + {0x00001418, 0x00000e00}, /*DDR SDRAM Operation Register */ + {0x0000141C, 0x00000672}, /*DDR SDRAM Mode Register */ + {0x00001420, 0x00000004}, /*DDR SDRAM Extended Mode Register */ + {0x00001424, 0x0100D3FF}, /*Dunit Control High Register */ + {0x00001428, 0x000D6720}, /*Dunit Control High Register */ + {0x0000142C, 0x014C2F38}, /*Dunit Control High Register */ + {0x0000147C, 0x00006571}, + + {0x00001494, 0x00010000}, /*DDR SDRAM ODT Control (Low) Register */ + {0x00001498, 0x00000000}, /*DDR SDRAM ODT Control (High) Register */ + {0x0000149C, 0x00000301}, /*DDR Dunit ODT Control Register */ + + {0x000014a0, 0x000002A9}, + {0x000014a8, 0x00000101}, /*2:1 */ + {0x00020220, 0x00000007}, + + {0x000014C0, 0x192424C8}, /* DRAM address and Control Driving Strenght */ + {0x000014C4, 0xEFB24C8}, /* DRAM Data and DQS Driving Strenght */ + + {0x000200e8, 0x3FFF0E01}, /* DO NOT Modify - Open Mbus Window - 2G - Mbus is required for the training sequence */ + {0x00020184, 0x3FFFFFE0}, /* DO NOT Modify - Close fast path Window to - 2G */ + + {0x0001504, 0x7FFFFFF1}, /* CS0 Size */ + {0x000150C, 0x00000000}, /* CS1 Size */ + {0x0001514, 0x00000000}, /* CS2 Size */ + {0x000151C, 0x00000000}, /* CS3 Size */ + + {0x00001538, 0x00000008}, /*Read Data Sample Delays Register */ + {0x0000153C, 0x0000000A}, /*Read Data Ready Delay Register */ + + {0x000015D0, 0x00000630}, /*MR0 */ + {0x000015D4, 0x00000046}, /*MR1 */ + {0x000015D8, 0x00000008}, /*MR2 */ + {0x000015DC, 0x00000000}, /*MR3 */ + + {0x000015E4, 0x00203c18}, /*ZQDS Configuration Register */ + /* {0x000015EC, 0xDE000025}, *//*DDR PHY */ + {0x000015EC, 0xF800AA25}, /*DDR PHY */ + {0x0, 0x0} +}; + +MV_DRAM_MC_INIT ddr3_Z1_db_600[MV_MAX_DDR3_STATIC_SIZE] = { +#ifdef MV_DDR_32BIT + {0x00001400, 0x73014A28}, /*DDR SDRAM Configuration Register */ +#else /*MV_DDR_64BIT */ + {0x00001400, 0x7301CA28}, /*DDR SDRAM Configuration Register */ +#endif + {0x00001404, 0x3630B040}, /*Dunit Control Low Register */ + {0x00001408, 0x44149887}, /*DDR SDRAM Timing (Low) Register */ + /* {0x0000140C, 0x38000C6A}, *//*DDR SDRAM Timing (High) Register */ + {0x0000140C, 0x38D83FE0}, /*DDR SDRAM Timing (High) Register */ + +#ifdef DB_78X60_PCAC + {0x00001410, 0x040F0001}, /*DDR SDRAM Address Control Register */ +#else + {0x00001410, 0x040F0000}, /*DDR SDRAM Open Pages Control Register */ +#endif + + {0x00001414, 0x00000000}, /*DDR SDRAM Open Pages Control Register */ + {0x00001418, 0x00000e00}, /*DDR SDRAM Operation Register */ + {0x00001420, 0x00000004}, /*DDR SDRAM Extended Mode Register */ + {0x00001424, 0x0100D1FF}, /*Dunit Control High Register */ + {0x00001428, 0x000F8830}, /*Dunit Control High Register */ + {0x0000142C, 0x214C2F38}, /*Dunit Control High Register */ + {0x0000147C, 0x0000c671}, + + {0x000014a8, 0x00000101}, /*2:1 */ + {0x00020220, 0x00000007}, + + {0x00001494, 0x00010000}, /*DDR SDRAM ODT Control (Low) Register */ + {0x00001498, 0x00000000}, /*DDR SDRAM ODT Control (High) Register */ + {0x0000149C, 0x00000301}, /*DDR Dunit ODT Control Register */ + + {0x000014C0, 0x192424C8}, /* DRAM address and Control Driving Strenght */ + {0x000014C4, 0xEFB24C8}, /* DRAM Data and DQS Driving Strenght */ + + {0x000200e8, 0x3FFF0E01}, /* DO NOT Modify - Open Mbus Window - 2G - Mbus is required for the training sequence */ + {0x00020184, 0x3FFFFFE0}, /* DO NOT Modify - Close fast path Window to - 2G */ + + {0x0001504, 0x7FFFFFF1}, /* CS0 Size */ + {0x000150C, 0x00000000}, /* CS1 Size */ + {0x0001514, 0x00000000}, /* CS2 Size */ + {0x000151C, 0x00000000}, /* CS3 Size */ + + /* {0x00001524, 0x0000C800}, */ + {0x00001538, 0x0000000b}, /*Read Data Sample Delays Register */ + {0x0000153C, 0x0000000d}, /*Read Data Ready Delay Register */ + + {0x000015D0, 0x00000650}, /*MR0 */ + {0x000015D4, 0x00000046}, /*MR1 */ + {0x000015D8, 0x00000010}, /*MR2 */ + {0x000015DC, 0x00000000}, /*MR3 */ + + {0x000015E4, 0x00203c18}, /*ZQC Configuration Register */ + {0x000015EC, 0xDE000025}, /*DDR PHY */ + {0x0, 0x0} +}; + +MV_DRAM_MC_INIT ddr3_Z1_db_300[MV_MAX_DDR3_STATIC_SIZE] = { +#ifdef MV_DDR_32BIT + {0x00001400, 0x73004C30}, /*DDR SDRAM Configuration Register */ +#else /*MV_DDR_64BIT */ + {0x00001400, 0x7300CC30}, /*DDR SDRAM Configuration Register */ + /*{0x00001400, 0x7304CC30}, *//*DDR SDRAM Configuration Register */ +#endif + {0x00001404, 0x3630B840}, /*Dunit Control Low Register */ + {0x00001408, 0x33137663}, /*DDR SDRAM Timing (Low) Register */ + {0x0000140C, 0x38000C55}, /*DDR SDRAM Timing (High) Register */ + {0x00001410, 0x040F0000}, /*DDR SDRAM Address Control Register */ + {0x00001414, 0x00000000}, /*DDR SDRAM Open Pages Control Register */ + {0x00001418, 0x00000e00}, /*DDR SDRAM Operation Register */ + {0x0000141C, 0x00000672}, /*DDR SDRAM Mode Register */ + {0x00001420, 0x00000004}, /*DDR SDRAM Extended Mode Register */ + {0x00001424, 0x0100F1FF}, /*Dunit Control High Register */ + {0x00001428, 0x000D6720}, /*Dunit Control High Register */ + {0x0000142C, 0x014C2F38}, /*Dunit Control High Register */ + {0x0000147C, 0x00006571}, + + {0x00001494, 0x00010000}, /*DDR SDRAM ODT Control (Low) Register */ + {0x00001498, 0x00000000}, /*DDR SDRAM ODT Control (High) Register */ + {0x0000149C, 0x00000301}, /*DDR Dunit ODT Control Register */ + + {0x000014C0, 0x192424C8}, /* DRAM address and Control Driving Strenght */ + {0x000014C4, 0xEFB24C8}, /* DRAM Data and DQS Driving Strenght */ + + {0x000200e8, 0x3FFF0E01}, /* DO NOT Modify - Open Mbus Window - 2G - Mbus is required for the training sequence */ + {0x00020184, 0x3FFFFFE0}, /* DO NOT Modify - Close fast path Window to - 2G */ + + {0x0001504, 0x7FFFFFF1}, /* CS0 Size */ + {0x000150C, 0x00000000}, /* CS1 Size */ + {0x0001514, 0x00000000}, /* CS2 Size */ + {0x000151C, 0x00000000}, /* CS3 Size */ + + {0x00001538, 0x00000008}, /*Read Data Sample Delays Register */ + {0x0000153C, 0x0000000A}, /*Read Data Ready Delay Register */ + + {0x000015D0, 0x00000630}, /*MR0 */ + {0x000015D4, 0x00000046}, /*MR1 */ + {0x000015D8, 0x00000008}, /*MR2 */ + {0x000015DC, 0x00000000}, /*MR3 */ + + {0x000015E4, 0x00203c18}, /*ZQDS Configuration Register */ + {0x000015EC, 0xDE000025}, /*DDR PHY */ + + {0x0, 0x0} +}; + +#endif /* __AXP_MC_STATIC_H */ diff --git a/drivers/ddr/mvebu/ddr3_axp_training_static.h b/drivers/ddr/mvebu/ddr3_axp_training_static.h new file mode 100644 index 0000000..4e61547 --- /dev/null +++ b/drivers/ddr/mvebu/ddr3_axp_training_static.h @@ -0,0 +1,770 @@ +/* + * Copyright (C) Marvell International Ltd. and its affiliates + * + * SPDX-License-Identifier: GPL-2.0 + */ + +#ifndef __AXP_TRAINING_STATIC_H +#define __AXP_TRAINING_STATIC_H + +/* + * STATIC_TRAINING - Set only if static parameters for training are set and + * required + */ + +MV_DRAM_TRAINING_INIT ddr3_db_rev2_667[MV_MAX_DDR3_STATIC_SIZE] = { + /* Read Leveling */ + /*PUP RdSampleDly (+CL) Phase RL ADLL value */ + /*0 */ + {0x000016A0, 0xC002011A}, + /*1 */ + {0x000016A0, 0xC0420100}, + /*2 */ + {0x000016A0, 0xC082020A}, + /*3 */ + {0x000016A0, 0xC0C20017}, + /*4 */ + {0x000016A0, 0xC1020113}, + /*5 */ + {0x000016A0, 0xC1420107}, + /*6 */ + {0x000016A0, 0xC182011F}, + /*7 */ + {0x000016A0, 0xC1C2001C}, + /*8 */ + {0x000016A0, 0xC202010D}, + + /* Write Leveling */ + /*0 */ + {0x000016A0, 0xC0004A06}, + /*1 */ + {0x000016A0, 0xC040690D}, + /*2 */ + {0x000016A0, 0xC0806A0D}, + /*3 */ + {0x000016A0, 0xC0C0A01B}, + /*4 */ + {0x000016A0, 0xC1003A01}, + /*5 */ + {0x000016A0, 0xC1408113}, + /*6 */ + {0x000016A0, 0xC1805609}, + /*7 */ + {0x000016A0, 0xC1C04504}, + /*8 */ + {0x000016A0, 0xC2009518}, + + /*center DQS on read cycle */ + {0x000016A0, 0xC803000F}, + + {0x00001538, 0x0000000B}, /*Read Data Sample Delays Register */ + {0x0000153C, 0x0000000F}, /*Read Data Ready Delay Register */ + + /*init DRAM */ + {0x00001480, 0x00000001}, + {0x0, 0x0} +}; + +MV_DRAM_TRAINING_INIT ddr3_db_rev2_800[MV_MAX_DDR3_STATIC_SIZE] = { + /* Read Leveling */ + /*PUP RdSampleDly (+CL) Phase RL ADLL value */ + /*0 */ + {0x000016A0, 0xC0020301}, + /*1 */ + {0x000016A0, 0xC0420202}, + /*2 */ + {0x000016A0, 0xC0820314}, + /*3 */ + {0x000016A0, 0xC0C20117}, + /*4 */ + {0x000016A0, 0xC1020219}, + /*5 */ + {0x000016A0, 0xC142020B}, + /*6 */ + {0x000016A0, 0xC182030A}, + /*7 */ + {0x000016A0, 0xC1C2011D}, + /*8 */ + {0x000016A0, 0xC2020212}, + + /* Write Leveling */ + /*0 */ + {0x000016A0, 0xC0007A12}, + /*1 */ + {0x000016A0, 0xC0408D16}, + /*2 */ + {0x000016A0, 0xC0809E1B}, + /*3 */ + {0x000016A0, 0xC0C0AC1F}, + /*4 */ + {0x000016A0, 0xC1005E0A}, + /*5 */ + {0x000016A0, 0xC140A91D}, + /*6 */ + {0x000016A0, 0xC1808E17}, + /*7 */ + {0x000016A0, 0xC1C05509}, + /*8 */ + {0x000016A0, 0xC2003A01}, + + /* PBS Leveling */ + /*0 */ + {0x000016A0, 0xC0007A12}, + /*1 */ + {0x000016A0, 0xC0408D16}, + /*2 */ + {0x000016A0, 0xC0809E1B}, + /*3 */ + {0x000016A0, 0xC0C0AC1F}, + /*4 */ + {0x000016A0, 0xC1005E0A}, + /*5 */ + {0x000016A0, 0xC140A91D}, + /*6 */ + {0x000016A0, 0xC1808E17}, + /*7 */ + {0x000016A0, 0xC1C05509}, + /*8 */ + {0x000016A0, 0xC2003A01}, + + /*center DQS on read cycle */ + {0x000016A0, 0xC803000B}, + + {0x00001538, 0x0000000D}, /*Read Data Sample Delays Register */ + {0x0000153C, 0x00000011}, /*Read Data Ready Delay Register */ + + /*init DRAM */ + {0x00001480, 0x00000001}, + {0x0, 0x0} +}; + +MV_DRAM_TRAINING_INIT ddr3_db_400[MV_MAX_DDR3_STATIC_SIZE] = { + /* Read Leveling */ + /*PUP RdSampleDly (+CL) Phase RL ADLL value */ + /*0 2 4 15 */ + {0x000016A0, 0xC002010C}, + /*1 2 4 2 */ + {0x000016A0, 0xC042001C}, + /*2 2 4 27 */ + {0x000016A0, 0xC0820115}, + /*3 2 4 0 */ + {0x000016A0, 0xC0C20019}, + /*4 2 4 13 */ + {0x000016A0, 0xC1020108}, + /*5 2 4 5 */ + {0x000016A0, 0xC1420100}, + /*6 2 4 19 */ + {0x000016A0, 0xC1820111}, + /*7 2 4 0 */ + {0x000016A0, 0xC1C2001B}, + /*8 2 4 10 */ + /*{0x000016A0, 0xC2020117}, */ + {0x000016A0, 0xC202010C}, + + /* Write Leveling */ + /*0 */ + {0x000016A0, 0xC0005508}, + /*1 */ + {0x000016A0, 0xC0409819}, + /*2 */ + {0x000016A0, 0xC080650C}, + /*3 */ + {0x000016A0, 0xC0C0700F}, + /*4 */ + {0x000016A0, 0xC1004103}, + /*5 */ + {0x000016A0, 0xC140A81D}, + /*6 */ + {0x000016A0, 0xC180650C}, + /*7 */ + {0x000016A0, 0xC1C08013}, + /*8 */ + {0x000016A0, 0xC2005508}, + + /*center DQS on read cycle */ + {0x000016A0, 0xC803000F}, + + {0x00001538, 0x00000008}, /*Read Data Sample Delays Register */ + {0x0000153C, 0x0000000A}, /*Read Data Ready Delay Register */ + + /*init DRAM */ + {0x00001480, 0x00000001}, + {0x0, 0x0} +}; + +MV_DRAM_TRAINING_INIT ddr3_db_533[MV_MAX_DDR3_STATIC_SIZE] = { + /* Read Leveling */ + /*PUP RdSampleDly (+CL) Phase RL ADLL value */ + /*0 2 4 15 */ + {0x000016A0, 0xC002040C}, + /*1 2 4 2 */ + {0x000016A0, 0xC0420117}, + /*2 2 4 27 */ + {0x000016A0, 0xC082041B}, + /*3 2 4 0 */ + {0x000016A0, 0xC0C20117}, + /*4 2 4 13 */ + {0x000016A0, 0xC102040A}, + /*5 2 4 5 */ + {0x000016A0, 0xC1420117}, + /*6 2 4 19 */ + {0x000016A0, 0xC1820419}, + /*7 2 4 0 */ + {0x000016A0, 0xC1C20117}, + /*8 2 4 10 */ + {0x000016A0, 0xC2020117}, + + /* Write Leveling */ + /*0 */ + {0x000016A0, 0xC0008113}, + /*1 */ + {0x000016A0, 0xC0404504}, + /*2 */ + {0x000016A0, 0xC0808514}, + /*3 */ + {0x000016A0, 0xC0C09418}, + /*4 */ + {0x000016A0, 0xC1006D0E}, + /*5 */ + {0x000016A0, 0xC1405508}, + /*6 */ + {0x000016A0, 0xC1807D12}, + /*7 */ + {0x000016A0, 0xC1C0b01F}, + /*8 */ + {0x000016A0, 0xC2005D0A}, + + /*center DQS on read cycle */ + {0x000016A0, 0xC803000F}, + + {0x00001538, 0x00000008}, /*Read Data Sample Delays Register */ + {0x0000153C, 0x0000000A}, /*Read Data Ready Delay Register */ + + /*init DRAM */ + {0x00001480, 0x00000001}, + {0x0, 0x0} +}; + +MV_DRAM_TRAINING_INIT ddr3_db_600[MV_MAX_DDR3_STATIC_SIZE] = { + /* Read Leveling */ + /*PUP RdSampleDly (+CL) Phase RL ADLL value */ + /*0 2 3 1 */ + {0x000016A0, 0xC0020104}, + /*1 2 2 6 */ + {0x000016A0, 0xC0420010}, + /*2 2 3 16 */ + {0x000016A0, 0xC0820112}, + /*3 2 1 26 */ + {0x000016A0, 0xC0C20009}, + /*4 2 2 29 */ + {0x000016A0, 0xC102001F}, + /*5 2 2 13 */ + {0x000016A0, 0xC1420014}, + /*6 2 3 6 */ + {0x000016A0, 0xC1820109}, + /*7 2 1 31 */ + {0x000016A0, 0xC1C2000C}, + /*8 2 2 22 */ + {0x000016A0, 0xC2020112}, + + /* Write Leveling */ + /*0 */ + {0x000016A0, 0xC0009919}, + /*1 */ + {0x000016A0, 0xC0405508}, + /*2 */ + {0x000016A0, 0xC0809919}, + /*3 */ + {0x000016A0, 0xC0C09C1A}, + /*4 */ + {0x000016A0, 0xC1008113}, + /*5 */ + {0x000016A0, 0xC140650C}, + /*6 */ + {0x000016A0, 0xC1809518}, + /*7 */ + {0x000016A0, 0xC1C04103}, + /*8 */ + {0x000016A0, 0xC2006D0E}, + + /*center DQS on read cycle */ + {0x000016A0, 0xC803000F}, + + {0x00001538, 0x0000000B}, /*Read Data Sample Delays Register */ + {0x0000153C, 0x0000000F}, /*Read Data Ready Delay Register */ + /*init DRAM */ + {0x00001480, 0x00000001}, + {0x0, 0x0} +}; + +MV_DRAM_TRAINING_INIT ddr3_db_667[MV_MAX_DDR3_STATIC_SIZE] = { + + /* Read Leveling */ + /*PUP RdSampleDly (+CL) Phase RL ADLL value */ + /*0 2 3 1 */ + {0x000016A0, 0xC0020103}, + /*1 2 2 6 */ + {0x000016A0, 0xC0420012}, + /*2 2 3 16 */ + {0x000016A0, 0xC0820113}, + /*3 2 1 26 */ + {0x000016A0, 0xC0C20012}, + /*4 2 2 29 */ + {0x000016A0, 0xC1020100}, + /*5 2 2 13 */ + {0x000016A0, 0xC1420016}, + /*6 2 3 6 */ + {0x000016A0, 0xC1820109}, + /*7 2 1 31 */ + {0x000016A0, 0xC1C20010}, + /*8 2 2 22 */ + {0x000016A0, 0xC2020112}, + + /* Write Leveling */ + /*0 */ + {0x000016A0, 0xC000b11F}, + /*1 */ + {0x000016A0, 0xC040690D}, + /*2 */ + {0x000016A0, 0xC0803600}, + /*3 */ + {0x000016A0, 0xC0C0a81D}, + /*4 */ + {0x000016A0, 0xC1009919}, + /*5 */ + {0x000016A0, 0xC1407911}, + /*6 */ + {0x000016A0, 0xC180ad1e}, + /*7 */ + {0x000016A0, 0xC1C04d06}, + /*8 */ + {0x000016A0, 0xC2008514}, + + /*center DQS on read cycle */ + {0x000016A0, 0xC803000F}, + + {0x00001538, 0x0000000B}, /*Read Data Sample Delays Register */ + {0x0000153C, 0x0000000F}, /*Read Data Ready Delay Register */ + + /*init DRAM */ + {0x00001480, 0x00000001}, + {0x0, 0x0} +}; + +MV_DRAM_TRAINING_INIT ddr3_db_800[MV_MAX_DDR3_STATIC_SIZE] = { + + /* Read Leveling */ + /*PUP RdSampleDly (+CL) Phase RL ADLL value */ + /*0 2 3 1 */ + {0x000016A0, 0xC0020213}, + /*1 2 2 6 */ + {0x000016A0, 0xC0420108}, + /*2 2 3 16 */ + {0x000016A0, 0xC0820210}, + /*3 2 1 26 */ + {0x000016A0, 0xC0C20108}, + /*4 2 2 29 */ + {0x000016A0, 0xC102011A}, + /*5 2 2 13 */ + {0x000016A0, 0xC1420300}, + /*6 2 3 6 */ + {0x000016A0, 0xC1820204}, + /*7 2 1 31 */ + {0x000016A0, 0xC1C20106}, + /*8 2 2 22 */ + {0x000016A0, 0xC2020112}, + + /* Write Leveling */ + /*0 */ + {0x000016A0, 0xC000620B}, + /*1 */ + {0x000016A0, 0xC0408D16}, + /*2 */ + {0x000016A0, 0xC0806A0D}, + /*3 */ + {0x000016A0, 0xC0C03D02}, + /*4 */ + {0x000016A0, 0xC1004a05}, + /*5 */ + {0x000016A0, 0xC140A11B}, + /*6 */ + {0x000016A0, 0xC1805E0A}, + /*7 */ + {0x000016A0, 0xC1C06D0E}, + /*8 */ + {0x000016A0, 0xC200AD1E}, + + /*center DQS on read cycle */ + {0x000016A0, 0xC803000F}, + + {0x00001538, 0x0000000C}, /*Read Data Sample Delays Register */ + {0x0000153C, 0x0000000E}, /*Read Data Ready Delay Register */ + + /*init DRAM */ + {0x00001480, 0x00000001}, + {0x0, 0x0} +}; + +MV_DRAM_TRAINING_INIT ddr3_rd_667_0[MV_MAX_DDR3_STATIC_SIZE] = { + /* Read Leveling */ + /*PUP RdSampleDly (+CL) Phase RL ADLL value */ + /*0 */ + {0x000016A0, 0xC002010E}, + /*1 */ + {0x000016A0, 0xC042001E}, + /*2 */ + {0x000016A0, 0xC0820118}, + /*3 */ + {0x000016A0, 0xC0C2001E}, + /*4 */ + {0x000016A0, 0xC102010C}, + /*5 */ + {0x000016A0, 0xC1420102}, + /*6 */ + {0x000016A0, 0xC1820111}, + /*7 */ + {0x000016A0, 0xC1C2001C}, + /*8 */ + {0x000016A0, 0xC2020109}, + + /* Write Leveling */ + /*0 */ + {0x000016A0, 0xC0003600}, + /*1 */ + {0x000016A0, 0xC040690D}, + /*2 */ + {0x000016A0, 0xC0805207}, + /*3 */ + {0x000016A0, 0xC0C0A81D}, + /*4 */ + {0x000016A0, 0xC1009919}, + /*5 */ + {0x000016A0, 0xC1407911}, + /*6 */ + {0x000016A0, 0xC1803E02}, + /*7 */ + {0x000016A0, 0xC1C05107}, + /*8 */ + {0x000016A0, 0xC2008113}, + + /*center DQS on read cycle */ + {0x000016A0, 0xC803000F}, + + {0x00001538, 0x0000000B}, /*Read Data Sample Delays Register */ + {0x0000153C, 0x0000000F}, /*Read Data Ready Delay Register */ + + /*init DRAM */ + {0x00001480, 0x00000001}, + {0x0, 0x0} +}; + +MV_DRAM_TRAINING_INIT ddr3_rd_667_1[MV_MAX_DDR3_STATIC_SIZE] = { + /* Read Leveling */ + /*PUP RdSampleDly (+CL) Phase RL ADLL value */ + /*0 */ + {0x000016A0, 0xC0020106}, + /*1 */ + {0x000016A0, 0xC0420016}, + /*2 */ + {0x000016A0, 0xC0820117}, + /*3 */ + {0x000016A0, 0xC0C2000F}, + /*4 */ + {0x000016A0, 0xC1020105}, + /*5 */ + {0x000016A0, 0xC142001B}, + /*6 */ + {0x000016A0, 0xC182010C}, + /*7 */ + {0x000016A0, 0xC1C20011}, + /*8 */ + {0x000016A0, 0xC2020101}, + + /* Write Leveling */ + /*0 */ + {0x000016A0, 0xC0003600}, + /*1 */ + {0x000016A0, 0xC0406D0E}, + /*2 */ + {0x000016A0, 0xC0803600}, + /*3 */ + {0x000016A0, 0xC0C04504}, + /*4 */ + {0x000016A0, 0xC1009919}, + /*5 */ + {0x000016A0, 0xC1407911}, + /*6 */ + {0x000016A0, 0xC1803600}, + /*7 */ + {0x000016A0, 0xC1C0610B}, + /*8 */ + {0x000016A0, 0xC2008113}, + + /*center DQS on read cycle */ + {0x000016A0, 0xC803000F}, + + {0x00001538, 0x0000000B}, /*Read Data Sample Delays Register */ + {0x0000153C, 0x0000000F}, /*Read Data Ready Delay Register */ + + /*init DRAM */ + {0x00001480, 0x00000001}, + {0x0, 0x0} +}; + +MV_DRAM_TRAINING_INIT ddr3_rd_667_2[MV_MAX_DDR3_STATIC_SIZE] = { + /* Read Leveling */ + /*PUP RdSampleDly (+CL) Phase RL ADLL value */ + /*0 */ + {0x000016A0, 0xC002010C}, + /*1 */ + {0x000016A0, 0xC042001B}, + /*2 */ + {0x000016A0, 0xC082011D}, + /*3 */ + {0x000016A0, 0xC0C20015}, + /*4 */ + {0x000016A0, 0xC102010B}, + /*5 */ + {0x000016A0, 0xC1420101}, + /*6 */ + {0x000016A0, 0xC1820113}, + /*7 */ + {0x000016A0, 0xC1C20017}, + /*8 */ + {0x000016A0, 0xC2020107}, + + /* Write Leveling */ + /*0 */ + {0x000016A0, 0xC0003600}, + /*1 */ + {0x000016A0, 0xC0406D0E}, + /*2 */ + {0x000016A0, 0xC0803600}, + /*3 */ + {0x000016A0, 0xC0C04504}, + /*4 */ + {0x000016A0, 0xC1009919}, + /*5 */ + {0x000016A0, 0xC1407911}, + /*6 */ + {0x000016A0, 0xC180B11F}, + /*7 */ + {0x000016A0, 0xC1C0610B}, + /*8 */ + {0x000016A0, 0xC2008113}, + + /*center DQS on read cycle */ + {0x000016A0, 0xC803000F}, + + {0x00001538, 0x0000000B}, /*Read Data Sample Delays Register */ + {0x0000153C, 0x0000000F}, /*Read Data Ready Delay Register */ + + /*init DRAM */ + {0x00001480, 0x00000001}, + {0x0, 0x0} +}; + +MV_DRAM_TRAINING_INIT ddr3_db_667_M[MV_MAX_DDR3_STATIC_SIZE] = { + /* Read Leveling */ + /*PUP RdSampleDly (+CL) Phase RL ADLL value */ + /* CS 0 */ + /*0 2 3 1 */ + {0x000016A0, 0xC0020103}, + /*1 2 2 6 */ + {0x000016A0, 0xC0420012}, + /*2 2 3 16 */ + {0x000016A0, 0xC0820113}, + /*3 2 1 26 */ + {0x000016A0, 0xC0C20012}, + /*4 2 2 29 */ + {0x000016A0, 0xC1020100}, + /*5 2 2 13 */ + {0x000016A0, 0xC1420016}, + /*6 2 3 6 */ + {0x000016A0, 0xC1820109}, + /*7 2 1 31 */ + {0x000016A0, 0xC1C20010}, + /*8 2 2 22 */ + {0x000016A0, 0xC2020112}, + + /* Write Leveling */ + /*0 */ + {0x000016A0, 0xC000b11F}, + /*1 */ + {0x000016A0, 0xC040690D}, + /*2 */ + {0x000016A0, 0xC0803600}, + /*3 */ + {0x000016A0, 0xC0C0a81D}, + /*4 */ + {0x000016A0, 0xC1009919}, + /*5 */ + {0x000016A0, 0xC1407911}, + /*6 */ + {0x000016A0, 0xC180ad1e}, + /*7 */ + {0x000016A0, 0xC1C04d06}, + /*8 */ + {0x000016A0, 0xC2008514}, + + /*center DQS on read cycle */ + {0x000016A0, 0xC803000F}, + + /* CS 1 */ + + {0x000016A0, 0xC0060103}, + /*1 2 2 6 */ + {0x000016A0, 0xC0460012}, + /*2 2 3 16 */ + {0x000016A0, 0xC0860113}, + /*3 2 1 26 */ + {0x000016A0, 0xC0C60012}, + /*4 2 2 29 */ + {0x000016A0, 0xC1060100}, + /*5 2 2 13 */ + {0x000016A0, 0xC1460016}, + /*6 2 3 6 */ + {0x000016A0, 0xC1860109}, + /*7 2 1 31 */ + {0x000016A0, 0xC1C60010}, + /*8 2 2 22 */ + {0x000016A0, 0xC2060112}, + + /* Write Leveling */ + /*0 */ + {0x000016A0, 0xC004b11F}, + /*1 */ + {0x000016A0, 0xC044690D}, + /*2 */ + {0x000016A0, 0xC0843600}, + /*3 */ + {0x000016A0, 0xC0C4a81D}, + /*4 */ + {0x000016A0, 0xC1049919}, + /*5 */ + {0x000016A0, 0xC1447911}, + /*6 */ + {0x000016A0, 0xC184ad1e}, + /*7 */ + {0x000016A0, 0xC1C44d06}, + /*8 */ + {0x000016A0, 0xC2048514}, + + /*center DQS on read cycle */ + {0x000016A0, 0xC807000F}, + + /* Both CS */ + + {0x00001538, 0x00000B0B}, /*Read Data Sample Delays Register */ + {0x0000153C, 0x00000F0F}, /*Read Data Ready Delay Register */ + + /*init DRAM */ + {0x00001480, 0x00000001}, + {0x0, 0x0} +}; + +MV_DRAM_TRAINING_INIT ddr3_rd_667_3[MV_MAX_DDR3_STATIC_SIZE] = { + /* Read Leveling */ + /*PUP RdSampleDly (+CL) Phase RL ADLL value */ + /*0 */ + {0x000016A0, 0xC0020118}, + /*1 */ + {0x000016A0, 0xC0420108}, + /*2 */ + {0x000016A0, 0xC0820202}, + /*3 */ + {0x000016A0, 0xC0C20108}, + /*4 */ + {0x000016A0, 0xC1020117}, + /*5 */ + {0x000016A0, 0xC142010C}, + /*6 */ + {0x000016A0, 0xC182011B}, + /*7 */ + {0x000016A0, 0xC1C20107}, + /*8 */ + {0x000016A0, 0xC2020113}, + + /* Write Leveling */ + /*0 */ + {0x000016A0, 0xC0003600}, + /*1 */ + {0x000016A0, 0xC0406D0E}, + /*2 */ + {0x000016A0, 0xC0805207}, + /*3 */ + {0x000016A0, 0xC0C0A81D}, + /*4 */ + {0x000016A0, 0xC1009919}, + /*5 */ + {0x000016A0, 0xC1407911}, + /*6 */ + {0x000016A0, 0xC1803E02}, + /*7 */ + {0x000016A0, 0xC1C04D06}, + /*8 */ + {0x000016A0, 0xC2008113}, + + /*center DQS on read cycle */ + {0x000016A0, 0xC803000F}, + + {0x00001538, 0x0000000B}, /*Read Data Sample Delays Register */ + {0x0000153C, 0x0000000F}, /*Read Data Ready Delay Register */ + + /*init DRAM */ + {0x00001480, 0x00000001}, + {0x0, 0x0} +}; + +MV_DRAM_TRAINING_INIT ddr3_pcac_600[MV_MAX_DDR3_STATIC_SIZE] = { + /* Read Leveling */ + /*PUP RdSampleDly (+CL) Phase RL ADLL value */ + /*0 */ + {0x000016A0, 0xC0020404}, + /* 1 2 2 6 */ + {0x000016A0, 0xC042031E}, + /* 2 2 3 16 */ + {0x000016A0, 0xC0820411}, + /* 3 2 1 26 */ + {0x000016A0, 0xC0C20400}, + /* 4 2 2 29 */ + {0x000016A0, 0xC1020404}, + /* 5 2 2 13 */ + {0x000016A0, 0xC142031D}, + /* 6 2 3 6 */ + {0x000016A0, 0xC182040C}, + /* 7 2 1 31 */ + {0x000016A0, 0xC1C2031B}, + /* 8 2 2 22 */ + {0x000016A0, 0xC2020112}, + + /* Write Leveling */ + /* 0 */ + {0x000016A0, 0xC0004905}, + /* 1 */ + {0x000016A0, 0xC040A81D}, + /* 2 */ + {0x000016A0, 0xC0804504}, + /* 3 */ + {0x000016A0, 0xC0C08013}, + /* 4 */ + {0x000016A0, 0xC1004504}, + /* 5 */ + {0x000016A0, 0xC140A81D}, + /* 6 */ + {0x000016A0, 0xC1805909}, + /* 7 */ + {0x000016A0, 0xC1C09418}, + /* 8 */ + {0x000016A0, 0xC2006D0E}, + + /*center DQS on read cycle */ + {0x000016A0, 0xC803000F}, + {0x00001538, 0x00000009}, /*Read Data Sample Delays Register */ + {0x0000153C, 0x0000000D}, /*Read Data Ready Delay Register */ + /* init DRAM */ + {0x00001480, 0x00000001}, + {0x0, 0x0} +}; + +#endif /* __AXP_TRAINING_STATIC_H */ diff --git a/drivers/ddr/mvebu/ddr3_axp_vars.h b/drivers/ddr/mvebu/ddr3_axp_vars.h new file mode 100644 index 0000000..1b0ab56 --- /dev/null +++ b/drivers/ddr/mvebu/ddr3_axp_vars.h @@ -0,0 +1,226 @@ +/* + * Copyright (C) Marvell International Ltd. and its affiliates + * + * SPDX-License-Identifier: GPL-2.0 + */ + +#ifndef __AXP_VARS_H +#define __AXP_VARS_H + +#include "ddr3_axp_config.h" +#include "ddr3_axp_mc_static.h" +#include "ddr3_axp_training_static.h" + +MV_DRAM_MODES ddr_modes[MV_DDR3_MODES_NUMBER] = { + /* Conf name CPUFreq FabFreq Chip ID Chip/Board MC regs Training Values */ + /* db board values: */ + {"db_800-400", 0xA, 0x5, 0x0, A0, ddr3_A0_db_400, NULL}, + {"db_1200-300", 0x2, 0xC, 0x0, A0, ddr3_A0_db_400, NULL}, + {"db_1200-600", 0x2, 0x5, 0x0, A0, NULL, NULL}, + {"db_1333-667", 0x3, 0x5, 0x0, A0, ddr3_A0_db_667, ddr3_db_rev2_667}, + {"db_1600-800", 0xB, 0x5, 0x0, A0, ddr3_A0_db_667, ddr3_db_rev2_800}, + {"amc_1333-667", 0x3, 0x5, 0x0, A0_AMC, ddr3_A0_AMC_667, NULL}, + {"db_667-667", 0x9, 0x13, 0x0, Z1, ddr3_Z1_db_600, ddr3_db_667}, + {"db_800-400", 0xA, 0x1, 0x0, Z1, ddr3_Z1_db_300, ddr3_db_400}, + {"db_1066-533", 0x1, 0x1, 0x0, Z1, ddr3_Z1_db_300, ddr3_db_533}, + {"db_1200-300", 0x2, 0xC, 0x0, Z1, ddr3_Z1_db_300, ddr3_db_667}, + {"db_1200-600", 0x2, 0x5, 0x0, Z1, ddr3_Z1_db_600, NULL}, + {"db_1333-333", 0x3, 0xC, 0x0, Z1, ddr3_Z1_db_300, ddr3_db_400}, + {"db_1333-667", 0x3, 0x5, 0x0, Z1, ddr3_Z1_db_600, ddr3_db_667}, + /* pcac board values (Z1 device): */ + {"pcac_1200-600", 0x2, 0x5, 0x0, Z1_PCAC, ddr3_Z1_db_600, + ddr3_pcac_600}, + /* rd board values (Z1 device): */ + {"rd_667_0", 0x3, 0x5, 0x0, Z1_RD_SLED, ddr3_Z1_db_600, ddr3_rd_667_0}, + {"rd_667_1", 0x3, 0x5, 0x1, Z1_RD_SLED, ddr3_Z1_db_600, ddr3_rd_667_1}, + {"rd_667_2", 0x3, 0x5, 0x2, Z1_RD_SLED, ddr3_Z1_db_600, ddr3_rd_667_2}, + {"rd_667_3", 0x3, 0x5, 0x3, Z1_RD_SLED, ddr3_Z1_db_600, ddr3_rd_667_3} +}; + +/* ODT settings - if needed update the following tables: (ODT_OPT - represents the CS configuration bitmap) */ + +u16 odt_static[ODT_OPT][MAX_CS] = { /* NearEnd/FarEnd */ + {0, 0, 0, 0}, /* 0000 0/0 - Not supported */ + {ODT40, 0, 0, 0}, /* 0001 0/1 */ + {0, 0, 0, 0}, /* 0010 0/0 - Not supported */ + {ODT40, ODT40, 0, 0}, /* 0011 0/2 */ + {0, 0, ODT40, 0}, /* 0100 1/0 */ + {ODT30, 0, ODT30, 0}, /* 0101 1/1 */ + {0, 0, 0, 0}, /* 0110 0/0 - Not supported */ + {ODT120, ODT20, ODT20, 0}, /* 0111 1/2 */ + {0, 0, 0, 0}, /* 1000 0/0 - Not supported */ + {0, 0, 0, 0}, /* 1001 0/0 - Not supported */ + {0, 0, 0, 0}, /* 1010 0/0 - Not supported */ + {0, 0, 0, 0}, /* 1011 0/0 - Not supported */ + {0, 0, ODT40, 0}, /* 1100 2/0 */ + {ODT20, 0, ODT120, ODT20}, /* 1101 2/1 */ + {0, 0, 0, 0}, /* 1110 0/0 - Not supported */ + {ODT120, ODT30, ODT120, ODT30} /* 1111 2/2 */ +}; + +u16 odt_dynamic[ODT_OPT][MAX_CS] = { /* NearEnd/FarEnd */ + {0, 0, 0, 0}, /* 0000 0/0 */ + {0, 0, 0, 0}, /* 0001 0/1 */ + {0, 0, 0, 0}, /* 0010 0/0 - Not supported */ + {0, 0, 0, 0}, /* 0011 0/2 */ + {0, 0, 0, 0}, /* 0100 1/0 */ + {ODT120D, 0, ODT120D, 0}, /* 0101 1/1 */ + {0, 0, 0, 0}, /* 0110 0/0 - Not supported */ + {0, 0, ODT120D, 0}, /* 0111 1/2 */ + {0, 0, 0, 0}, /* 1000 0/0 - Not supported */ + {0, 0, 0, 0}, /* 1001 0/0 - Not supported */ + {0, 0, 0, 0}, /* 1010 0/0 - Not supported */ + {0, 0, 0, 0}, /* 1011 0/0 - Not supported */ + {0, 0, 0, 0}, /* 1100 2/0 */ + {ODT120D, 0, 0, 0}, /* 1101 2/1 */ + {0, 0, 0, 0}, /* 1110 0/0 - Not supported */ + {0, 0, 0, 0} /* 1111 2/2 */ +}; + +u32 odt_config[ODT_OPT] = { + 0, 0x00010000, 0, 0x00030000, 0x04000000, 0x05050104, 0, 0x07430340, 0, + 0, 0, 0, + 0x30000, 0x1C0D100C, 0, 0x3CC330C0 +}; + +/* + * User can manually set SPD values (in case SPD is not available on + * DIMM/System). + * SPD Values can simplify calculating the DUNIT registers values + */ +u8 spd_data[SPD_SIZE] = { + /* AXP DB Board DIMM SPD Values - manually set */ + 0x92, 0x10, 0x0B, 0x2, 0x3, 0x19, 0x0, 0x9, 0x09, 0x52, 0x1, 0x8, 0x0C, + 0x0, 0x7E, 0x0, 0x69, 0x78, + 0x69, 0x30, 0x69, 0x11, 0x20, 0x89, 0x0, 0x5, 0x3C, 0x3C, 0x0, 0xF0, + 0x82, 0x5, 0x80, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0F, 0x1, 0x3, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x80, 0x2C, 0x1, 0x10, 0x23, 0x35, 0x28, 0xEB, 0xCA, 0x19, 0x8F +}; + +/* + * Controller Specific configurations Starts Here - DO NOT MODIFY + */ + +/* Frequency - values are 1/HCLK in ps */ +u32 cpu_fab_clk_to_hclk[FAB_OPT][CLK_CPU] = +/* CPU Frequency: + 1000 1066 1200 1333 1500 1666 1800 2000 600 667 800 1600 Fabric */ +{ + {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0, 0, 0, 0, 0, 3000, 2500, 0}, + {0, 0, 0, 0, 0, 0, 0, 0, 0, 4500, 3750, 0}, + {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + {0, 0, 2500, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + {4000, 3750, 3333, 3000, 2666, 2400, 0, 0, 0, 0, 5000, 2500}, + {0, 0, 0, 0, 0, 0, 0, 0, 0, 3000, 0, 0}, + {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + {2500, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + {0, 0, 5000, 0, 4000, 0, 0, 0, 0, 0, 0, 3750}, + {5000, 0, 0, 3750, 3333, 0, 0, 0, 0, 0, 0, 3125}, + {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + {0, 0, 3330, 3000, 0, 0, 0, 0, 0, 0, 0, 2500}, + {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3750}, + {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0, 0, 0, 0, 0, 3000, 2500, 0}, + {3000, 0, 2500, 0, 0, 0, 0, 0, 0, 0, 3750, 0} +}; + +u32 cpu_ddr_ratios[FAB_OPT][CLK_CPU] = +/* CPU Frequency: + 1000 1066 1200 1333 1500 1666 1800 2000 600 667 800 1600 Fabric */ +{ + {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0, 0, 0, 0, 0, DDR_333, DDR_400, 0}, + {0, 0, 0, 0, 0, 0, 0, 0, 0, DDR_444, DDR_533, 0}, + {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + {0, 0, DDR_400, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + {DDR_500, DDR_533, DDR_600, DDR_666, DDR_750, DDR_833, 0, 0, 0, 0, + DDR_400, DDR_800}, + {0, 0, 0, 0, 0, 0, 0, 0, 0, DDR_333, 0, 0}, + {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + {DDR_400, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + {0, 0, DDR_400, 0, DDR_500, 0, 0, 0, 0, 0, 0, DDR_533}, + {DDR_400, 0, 0, DDR_533, DDR_600, 0, 0, 0, 0, 0, 0, DDR_640}, + {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + {0, 0, DDR_300, DDR_333, 0, 0, 0, 0, 0, 0, 0, DDR_400}, + {0, 0, 0, 0, 0, 0, DDR_600, DDR_666, 0, 0, 0, DDR_533}, + {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + {0, 0, 0, 0, 0, 0, 0, 0, 0, DDR_666, DDR_800, 0}, + {DDR_666, 0, DDR_800, 0, 0, 0, 0, 0, 0, 0, DDR_533, 0} +}; + +u8 div_ratio1to1[CLK_VCO][CLK_DDR] = +/* DDR Frequency: + 100 300 360 400 444 500 533 600 666 750 800 833 */ +{ {0xA, 3, 0, 3, 0, 2, 0, 0, 0, 0, 0, 0}, /* 1:1 CLK_CPU_1000 */ +{0xB, 3, 0, 3, 0, 0, 2, 0, 0, 0, 0, 0}, /* 1:1 CLK_CPU_1066 */ +{0xC, 4, 0, 3, 0, 0, 0, 2, 0, 0, 0, 0}, /* 1:1 CLK_CPU_1200 */ +{0xD, 4, 0, 4, 0, 0, 0, 0, 2, 0, 0, 0}, /* 1:1 CLK_CPU_1333 */ +{0xF, 5, 0, 4, 0, 3, 0, 0, 0, 0, 0, 0}, /* 1:1 CLK_CPU_1500 */ +{0x11, 5, 0, 4, 0, 0, 0, 0, 0, 0, 0, 0}, /* 1:1 CLK_CPU_1666 */ +{0x12, 6, 5, 4, 0, 0, 0, 3, 0, 0, 0, 0}, /* 1:1 CLK_CPU_1800 */ +{0x14, 7, 0, 5, 0, 4, 0, 0, 3, 0, 0, 0}, /* 1:1 CLK_CPU_2000 */ +{0x6, 2, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0}, /* 1:1 CLK_CPU_600 */ +{0x6, 2, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0}, /* 1:1 CLK_CPU_667 */ +{0x8, 2, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0}, /* 1:1 CLK_CPU_800 */ +{0x10, 5, 0, 4, 0, 0, 0, 0, 0, 0, 0, 0}, /* 1:1 CLK_CPU_1600 */ +{0x14, 0, 0, 5, 0, 0, 0, 0, 0, 0, 0, 0}, /* 1:1 CLK_CPU_1000 VCO_2000 */ +{0x15, 0, 0, 6, 0, 0, 0, 0, 0, 0, 0, 0}, /* 1:1 CLK_CPU_1066 VCO_2133 */ +{0x18, 0, 0, 6, 0, 0, 0, 0, 0, 0, 0, 0}, /* 1:1 CLK_CPU_1200 VCO_2400 */ +{0x1A, 0, 0, 7, 0, 0, 0, 0, 0, 0, 0, 0}, /* 1:1 CLK_CPU_1333 VCO_2666 */ +{0x1E, 0, 0, 8, 0, 0, 0, 0, 0, 0, 0, 0}, /* 1:1 CLK_CPU_1500 VCO_3000 */ +{0x21, 0, 0, 9, 0, 0, 0, 0, 0, 0, 0, 0}, /* 1:1 CLK_CPU_1666 VCO_3333 */ +{0x24, 0, 0, 9, 0, 0, 0, 0, 0, 0, 0, 0}, /* 1:1 CLK_CPU_1800 VCO_3600 */ +{0x28, 0, 0, 10, 0, 0, 0, 0, 0, 0, 0, 0}, /* 1:1 CLK_CPU_2000 VCO_4000 */ +{0xC, 0, 0, 3, 0, 0, 0, 0, 0, 0, 0, 0}, /* 1:1 CLK_CPU_600 VCO_1200 */ +{0xD, 0, 0, 4, 0, 0, 0, 0, 0, 0, 0, 0}, /* 1:1 CLK_CPU_667 VCO_1333 */ +{0x10, 0, 0, 4, 0, 0, 0, 0, 0, 0, 0, 0}, /* 1:1 CLK_CPU_800 VCO_1600 */ +{0x20, 10, 0, 8, 0, 0, 0, 0, 0, 0, 0, 0} /* 1:1 CLK_CPU_1600 VCO_3200 */ +}; + +u8 div_ratio2to1[CLK_VCO][CLK_DDR] = +/* DDR Frequency: + 100 300 360 400 444 500 533 600 666 750 800 833 */ +{ {0, 0, 0, 0, 0, 2, 0, 0, 3, 0, 0, 0}, /* 2:1 CLK_CPU_1000 */ +{0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0}, /* 2:1 CLK_CPU_1066 */ +{0, 0, 0, 3, 5, 0, 0, 2, 0, 0, 3, 3}, /* 2:1 CLK_CPU_1200 */ +{0, 0, 0, 0, 0, 0, 5, 0, 2, 0, 3, 0}, /* 2:1 CLK_CPU_1333 */ +{0, 0, 0, 0, 0, 3, 0, 5, 0, 2, 0, 0}, /* 2:1 CLK_CPU_1500 */ +{0, 0, 0, 0, 0, 0, 0, 0, 5, 0, 0, 2}, /* 2:1 CLK_CPU_1666 */ +{0, 0, 0, 0, 0, 0, 0, 3, 0, 5, 0, 0}, /* 2:1 CLK_CPU_1800 */ +{0, 0, 0, 0, 0, 0, 0, 0, 3, 0, 0, 5}, /* 2:1 CLK_CPU_2000 */ +{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, /* 2:1 CLK_CPU_600 */ +{0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0}, /* 2:1 CLK_CPU_667 */ +{0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 1, 0}, /* 2:1 CLK_CPU_800 */ +{0, 0, 0, 0, 0, 0, 3, 0, 0, 0, 2, 0}, /* 2:1 CLK_CPU_1600 */ +{0, 0, 0, 5, 0, 0, 0, 0, 3, 0, 0, 0}, /* 2:1 CLK_CPU_1000 VCO_2000 */ +{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, /* 2:1 CLK_CPU_1066 VCO_2133 */ +{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 0}, /* 2:1 CLK_CPU_1200 VCO_2400 */ +{0, 0, 0, 0, 0, 0, 5, 0, 0, 0, 0, 0}, /* 2:1 CLK_CPU_1333 VCO_2666 */ +{0, 0, 0, 0, 0, 0, 0, 5, 0, 0, 0, 0}, /* 2:1 CLK_CPU_1500 VCO_3000 */ +{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, /* 2:1 CLK_CPU_1666 VCO_3333 */ +{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, /* 2:1 CLK_CPU_1800 VCO_3600 */ +{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, /* 2:1 CLK_CPU_2000 VCO_4000 */ +{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, /* 2:1 CLK_CPU_600 VCO_1200 */ +{0, 0, 0, 0, 3, 0, 0, 0, 0, 0, 0, 0}, /* 2:1 CLK_CPU_667 VCO_1333 */ +{0, 0, 0, 0, 0, 0, 3, 0, 0, 0, 0, 0}, /* 2:1 CLK_CPU_800 VCO_1600 */ +{0, 0, 0, 0, 0, 0, 0, 5, 5, 0, 0, 0} /* 2:1 CLK_CPU_1600 VCO_3200 */ +}; + +#endif /* __AXP_VARS_H */ diff --git a/drivers/ddr/mvebu/ddr3_dfs.c b/drivers/ddr/mvebu/ddr3_dfs.c new file mode 100644 index 0000000..9347773 --- /dev/null +++ b/drivers/ddr/mvebu/ddr3_dfs.c @@ -0,0 +1,1552 @@ +/* + * Copyright (C) Marvell International Ltd. and its affiliates + * + * SPDX-License-Identifier: GPL-2.0 + */ + +#include <common.h> +#include <i2c.h> +#include <spl.h> +#include <asm/io.h> +#include <asm/arch/cpu.h> +#include <asm/arch/soc.h> + +#include "ddr3_hw_training.h" + +/* + * Debug + */ +#define DEBUG_DFS_C(s, d, l) \ + DEBUG_DFS_S(s); DEBUG_DFS_D(d, l); DEBUG_DFS_S("\n") +#define DEBUG_DFS_FULL_C(s, d, l) \ + DEBUG_DFS_FULL_S(s); DEBUG_DFS_FULL_D(d, l); DEBUG_DFS_FULL_S("\n") + +#ifdef MV_DEBUG_DFS +#define DEBUG_DFS_S(s) puts(s) +#define DEBUG_DFS_D(d, l) printf("%x", d) +#else +#define DEBUG_DFS_S(s) +#define DEBUG_DFS_D(d, l) +#endif + +#ifdef MV_DEBUG_DFS_FULL +#define DEBUG_DFS_FULL_S(s) puts(s) +#define DEBUG_DFS_FULL_D(d, l) printf("%x", d) +#else +#define DEBUG_DFS_FULL_S(s) +#define DEBUG_DFS_FULL_D(d, l) +#endif + +#if defined(MV88F672X) +extern u8 div_ratio[CLK_VCO][CLK_DDR]; +extern void get_target_freq(u32 freq_mode, u32 *ddr_freq, u32 *hclk_ps); +#else +extern u16 odt_dynamic[ODT_OPT][MAX_CS]; +extern u8 div_ratio1to1[CLK_CPU][CLK_DDR]; +extern u8 div_ratio2to1[CLK_CPU][CLK_DDR]; +#endif +extern u16 odt_static[ODT_OPT][MAX_CS]; + +extern u32 cpu_fab_clk_to_hclk[FAB_OPT][CLK_CPU]; + +extern u32 ddr3_get_vco_freq(void); + +u32 ddr3_get_freq_parameter(u32 target_freq, int ratio_2to1); + +#ifdef MV_DEBUG_DFS +static inline void dfs_reg_write(u32 addr, u32 val) +{ + printf("\n write reg 0x%08x = 0x%08x", addr, val); + writel(val, INTER_REGS_BASE + addr); +} +#else +static inline void dfs_reg_write(u32 addr, u32 val) +{ + writel(val, INTER_REGS_BASE + addr); +} +#endif + +static void wait_refresh_op_complete(void) +{ + u32 reg; + + /* Poll - Wait for Refresh operation completion */ + do { + reg = reg_read(REG_SDRAM_OPERATION_ADDR) & + REG_SDRAM_OPERATION_CMD_RFRS_DONE; + } while (reg); /* Wait for '0' */ +} + +/* + * Name: ddr3_get_freq_parameter + * Desc: Finds CPU/DDR frequency ratio according to Sample@reset and table. + * Args: target_freq - target frequency + * Notes: + * Returns: freq_par - the ratio parameter + */ +u32 ddr3_get_freq_parameter(u32 target_freq, int ratio_2to1) +{ + u32 ui_vco_freq, freq_par; + + ui_vco_freq = ddr3_get_vco_freq(); + +#if defined(MV88F672X) + freq_par = div_ratio[ui_vco_freq][target_freq]; +#else + /* Find the ratio between PLL frequency and ddr-clk */ + if (ratio_2to1) + freq_par = div_ratio2to1[ui_vco_freq][target_freq]; + else + freq_par = div_ratio1to1[ui_vco_freq][target_freq]; +#endif + + return freq_par; +} + +/* + * Name: ddr3_dfs_high_2_low + * Desc: + * Args: freq - target frequency + * Notes: + * Returns: MV_OK - success, MV_FAIL - fail + */ +int ddr3_dfs_high_2_low(u32 freq, MV_DRAM_INFO *dram_info) +{ +#if defined(MV88F78X60) || defined(MV88F672X) + /* This Flow is relevant for ArmadaXP A0 */ + u32 reg, freq_par, tmp; + u32 cs = 0; + + DEBUG_DFS_C("DDR3 - DFS - High To Low - Starting DFS procedure to Frequency - ", + freq, 1); + + /* target frequency - 100MHz */ + freq_par = ddr3_get_freq_parameter(freq, 0); + +#if defined(MV88F672X) + u32 hclk; + u32 cpu_freq = ddr3_get_cpu_freq(); + get_target_freq(cpu_freq, &tmp, &hclk); +#endif + + /* Configure - DRAM DLL final state after DFS is complete - Enable */ + reg = reg_read(REG_DFS_ADDR); + /* [0] - DfsDllNextState - Disable */ + reg |= (1 << REG_DFS_DLLNEXTSTATE_OFFS); + dfs_reg_write(REG_DFS_ADDR, reg); /* 0x1528 - DFS register */ + + /* + * Configure - XBAR Retry response during Block to enable internal + * access - Disable + */ + reg = reg_read(REG_METAL_MASK_ADDR); + /* [0] - RetryMask - Disable */ + reg &= ~(1 << REG_METAL_MASK_RETRY_OFFS); + /* 0x14B0 - Dunit MMask Register */ + dfs_reg_write(REG_METAL_MASK_ADDR, reg); + + /* Configure - Block new external transactions - Enable */ + reg = reg_read(REG_DFS_ADDR); + reg |= (1 << REG_DFS_BLOCK_OFFS); /* [1] - DfsBlock - Enable */ + dfs_reg_write(REG_DFS_ADDR, reg); /* 0x1528 - DFS register */ + + /* Registered DIMM support */ + if (dram_info->reg_dimm) { + /* + * Configure - Disable Register DIMM CKE Power + * Down mode - CWA_RC + */ + reg = (0x9 & REG_SDRAM_OPERATION_CWA_RC_MASK) << + REG_SDRAM_OPERATION_CWA_RC_OFFS; + /* + * Configure - Disable Register DIMM CKE Power + * Down mode - CWA_DATA + */ + reg |= ((0 & REG_SDRAM_OPERATION_CWA_DATA_MASK) << + REG_SDRAM_OPERATION_CWA_DATA_OFFS); + + /* + * Configure - Disable Register DIMM CKE Power + * Down mode - Set Delay - tMRD + */ + reg |= (0 << REG_SDRAM_OPERATION_CWA_DELAY_SEL_OFFS); + + /* Configure - Issue CWA command with the above parameters */ + reg |= (REG_SDRAM_OPERATION_CMD_CWA & + ~(0xF << REG_SDRAM_OPERATION_CS_OFFS)); + + /* 0x1418 - SDRAM Operation Register */ + dfs_reg_write(REG_SDRAM_OPERATION_ADDR, reg); + + /* Poll - Wait for CWA operation completion */ + do { + reg = reg_read(REG_SDRAM_OPERATION_ADDR) & + (REG_SDRAM_OPERATION_CMD_MASK); + } while (reg); + + /* Configure - Disable outputs floating during Self Refresh */ + reg = reg_read(REG_REGISTERED_DRAM_CTRL_ADDR); + /* [15] - SRFloatEn - Disable */ + reg &= ~(1 << REG_REGISTERED_DRAM_CTRL_SR_FLOAT_OFFS); + /* 0x16D0 - DDR3 Registered DRAM Control */ + dfs_reg_write(REG_REGISTERED_DRAM_CTRL_ADDR, reg); + } + + /* Optional - Configure - DDR3_Rtt_nom_CS# */ + for (cs = 0; cs < MAX_CS; cs++) { + if (dram_info->cs_ena & (1 << cs)) { + reg = reg_read(REG_DDR3_MR1_CS_ADDR + + (cs << MR_CS_ADDR_OFFS)); + reg &= REG_DDR3_MR1_RTT_MASK; + dfs_reg_write(REG_DDR3_MR1_CS_ADDR + + (cs << MR_CS_ADDR_OFFS), reg); + } + } + + /* Configure - Move DRAM into Self Refresh */ + reg = reg_read(REG_DFS_ADDR); + reg |= (1 << REG_DFS_SR_OFFS); /* [2] - DfsSR - Enable */ + dfs_reg_write(REG_DFS_ADDR, reg); /* 0x1528 - DFS register */ + + /* Poll - Wait for Self Refresh indication */ + do { + reg = ((reg_read(REG_DFS_ADDR)) & (1 << REG_DFS_ATSR_OFFS)); + } while (reg == 0x0); /* 0x1528 [3] - DfsAtSR - Wait for '1' */ + + /* Start of clock change procedure (PLL) */ +#if defined(MV88F672X) + /* avantaLP */ + /* Configure cpupll_clkdiv_reset_mask */ + reg = reg_read(CPU_PLL_CLOCK_DIVIDER_CNTRL0); + reg &= CPU_PLL_CLOCK_DIVIDER_CNTRL0_MASK; + /* 0xE8264[7:0] 0xff CPU Clock Dividers Reset mask */ + dfs_reg_write(CPU_PLL_CLOCK_DIVIDER_CNTRL0, (reg + 0xFF)); + + /* Configure cpu_clkdiv_reload_smooth */ + reg = reg_read(CPU_PLL_CNTRL0); + reg &= CPU_PLL_CNTRL0_RELOAD_SMOOTH_MASK; + /* 0xE8260 [15:8] 0x2 CPU Clock Dividers Reload Smooth enable */ + dfs_reg_write(CPU_PLL_CNTRL0, + (reg + (2 << CPU_PLL_CNTRL0_RELOAD_SMOOTH_OFFS))); + + /* Configure cpupll_clkdiv_relax_en */ + reg = reg_read(CPU_PLL_CNTRL0); + reg &= CPU_PLL_CNTRL0_RELAX_EN_MASK; + /* 0xE8260 [31:24] 0x2 Relax Enable */ + dfs_reg_write(CPU_PLL_CNTRL0, + (reg + (2 << CPU_PLL_CNTRL0_RELAX_EN_OFFS))); + + /* Configure cpupll_clkdiv_ddr_clk_ratio */ + reg = reg_read(CPU_PLL_CLOCK_DIVIDER_CNTRL1); + /* + * 0xE8268 [13:8] N Set Training clock: + * APLL Out Clock (VCO freq) / N = 100 MHz + */ + reg &= CPU_PLL_CLOCK_DIVIDER_CNTRL1_MASK; + reg |= (freq_par << 8); /* full Integer ratio from PLL-out to ddr-clk */ + dfs_reg_write(CPU_PLL_CLOCK_DIVIDER_CNTRL1, reg); + + /* Configure cpupll_clkdiv_reload_ratio */ + reg = reg_read(CPU_PLL_CLOCK_DIVIDER_CNTRL0); + reg &= CPU_PLL_CLOCK_RELOAD_RATIO_MASK; + /* 0xE8264 [8]=0x1 CPU Clock Dividers Reload Ratio trigger set */ + dfs_reg_write(CPU_PLL_CLOCK_DIVIDER_CNTRL0, + (reg + (1 << CPU_PLL_CLOCK_RELOAD_RATIO_OFFS))); + + udelay(1); + + /* Configure cpupll_clkdiv_reload_ratio */ + reg = reg_read(CPU_PLL_CLOCK_DIVIDER_CNTRL0); + reg &= CPU_PLL_CLOCK_RELOAD_RATIO_MASK; + /* 0xE8264 [8]=0x0 CPU Clock Dividers Reload Ratio trigger clear */ + dfs_reg_write(CPU_PLL_CLOCK_DIVIDER_CNTRL0, reg); + + udelay(5); + +#else + /* + * Initial Setup - assure that the "load new ratio" is clear (bit 24) + * and in the same chance, block reassertions of reset [15:8] and + * force reserved bits[7:0]. + */ + reg = 0x0000FDFF; + /* 0x18700 - CPU Div CLK control 0 */ + dfs_reg_write(REG_CPU_DIV_CLK_CTRL_0_ADDR, reg); + + /* + * RelaX whenever reset is asserted to that channel + * (good for any case) + */ + reg = 0x0000FF00; + /* 0x18704 - CPU Div CLK control 0 */ + dfs_reg_write(REG_CPU_DIV_CLK_CTRL_1_ADDR, reg); + + reg = reg_read(REG_CPU_DIV_CLK_CTRL_2_ADDR) & + REG_CPU_DIV_CLK_CTRL_3_FREQ_MASK; + + /* full Integer ratio from PLL-out to ddr-clk */ + reg |= (freq_par << REG_CPU_DIV_CLK_CTRL_3_FREQ_OFFS); + /* 0x1870C - CPU Div CLK control 3 register */ + dfs_reg_write(REG_CPU_DIV_CLK_CTRL_2_ADDR, reg); + + /* + * Shut off clock enable to the DDRPHY clock channel (this is the "D"). + * All the rest are kept as is (forced, but could be read-modify-write). + * This is done now by RMW above. + */ + + /* Clock is not shut off gracefully - keep it running */ + reg = 0x000FFF02; + dfs_reg_write(REG_CPU_DIV_CLK_CTRL_4_ADDR, reg); + + /* Wait before replacing the clock on the DDR Phy Channel. */ + udelay(1); + + /* + * This for triggering the frequency update. Bit[24] is the + * central control + * bits [23:16] == which channels to change ==2 ==> + * only DDR Phy (smooth transition) + * bits [15:8] == mask reset reassertion due to clock modification + * to these channels. + * bits [7:0] == not in use + */ + reg = 0x0102FDFF; + /* 0x18700 - CPU Div CLK control 0 register */ + dfs_reg_write(REG_CPU_DIV_CLK_CTRL_0_ADDR, reg); + + udelay(1); /* Wait 1usec */ + + /* + * Poll Div CLK status 0 register - indication that the clocks + * are active - 0x18718 [8] + */ + do { + reg = (reg_read(REG_CPU_DIV_CLK_STATUS_0_ADDR)) & + (1 << REG_CPU_DIV_CLK_ALL_STABLE_OFFS); + } while (reg == 0); + + /* + * Clean the CTRL0, to be ready for next resets and next requests + * of ratio modifications. + */ + reg = 0x000000FF; + /* 0x18700 - CPU Div CLK control 0 register */ + dfs_reg_write(REG_CPU_DIV_CLK_CTRL_0_ADDR, reg); + + udelay(5); +#endif + /* End of clock change procedure (PLL) */ + + /* Configure - Select normal clock for the DDR PHY - Enable */ + reg = reg_read(REG_DRAM_INIT_CTRL_STATUS_ADDR); + /* [16] - ddr_phy_trn_clk_sel - Enable */ + reg |= (1 << REG_DRAM_INIT_CTRL_TRN_CLK_OFFS); + /* 0x18488 - DRAM Init control status register */ + dfs_reg_write(REG_DRAM_INIT_CTRL_STATUS_ADDR, reg); + + /* Configure - Set Correct Ratio - 1:1 */ + /* [15] - Phy2UnitClkRatio = 0 - Set 1:1 Ratio between Dunit and Phy */ + + reg = reg_read(REG_DDR_IO_ADDR) & ~(1 << REG_DDR_IO_CLK_RATIO_OFFS); + dfs_reg_write(REG_DDR_IO_ADDR, reg); /* 0x1524 - DDR IO Register */ + + /* Configure - 2T Mode - Restore original configuration */ + reg = reg_read(REG_DUNIT_CTRL_LOW_ADDR); + /* [3:4] 2T - 1T Mode - low freq */ + reg &= ~(REG_DUNIT_CTRL_LOW_2T_MASK << REG_DUNIT_CTRL_LOW_2T_OFFS); + /* 0x1404 - DDR Controller Control Low Register */ + dfs_reg_write(REG_DUNIT_CTRL_LOW_ADDR, reg); + + /* Configure - Restore CL and CWL - MRS Commands */ + reg = reg_read(REG_DFS_ADDR); + reg &= ~(REG_DFS_CL_NEXT_STATE_MASK << REG_DFS_CL_NEXT_STATE_OFFS); + reg &= ~(REG_DFS_CWL_NEXT_STATE_MASK << REG_DFS_CWL_NEXT_STATE_OFFS); + /* [8] - DfsCLNextState - MRS CL=6 after DFS (due to DLL-off mode) */ + reg |= (0x4 << REG_DFS_CL_NEXT_STATE_OFFS); + /* [12] - DfsCWLNextState - MRS CWL=6 after DFS (due to DLL-off mode) */ + reg |= (0x1 << REG_DFS_CWL_NEXT_STATE_OFFS); + dfs_reg_write(REG_DFS_ADDR, reg); /* 0x1528 - DFS register */ + + /* Poll - Wait for APLL + ADLLs lock on new frequency */ + do { + reg = (reg_read(REG_PHY_LOCK_STATUS_ADDR)) & + REG_PHY_LOCK_APLL_ADLL_STATUS_MASK; + /* 0x1674 [10:0] - Phy lock status Register */ + } while (reg != REG_PHY_LOCK_APLL_ADLL_STATUS_MASK); + + /* Configure - Reset the PHY Read FIFO and Write channels - Set Reset */ + reg = (reg_read(REG_SDRAM_CONFIG_ADDR) & REG_SDRAM_CONFIG_MASK); + /* [30:29] = 0 - Data Pup R/W path reset */ + /* 0x1400 - SDRAM Configuration register */ + dfs_reg_write(REG_SDRAM_CONFIG_ADDR, reg); + + /* + * Configure - DRAM Data PHY Read [30], Write [29] path + * reset - Release Reset + */ + reg = (reg_read(REG_SDRAM_CONFIG_ADDR) | ~REG_SDRAM_CONFIG_MASK); + /* [30:29] = '11' - Data Pup R/W path reset */ + /* 0x1400 - SDRAM Configuration register */ + dfs_reg_write(REG_SDRAM_CONFIG_ADDR, reg); + + /* Registered DIMM support */ + if (dram_info->reg_dimm) { + /* + * Configure - Change register DRAM operating speed + * (below 400MHz) - CWA_RC + */ + reg = (0xA & REG_SDRAM_OPERATION_CWA_RC_MASK) << + REG_SDRAM_OPERATION_CWA_RC_OFFS; + + /* + * Configure - Change register DRAM operating speed + * (below 400MHz) - CWA_DATA + */ + reg |= ((0x0 & REG_SDRAM_OPERATION_CWA_DATA_MASK) << + REG_SDRAM_OPERATION_CWA_DATA_OFFS); + + /* Configure - Set Delay - tSTAB */ + reg |= (0x1 << REG_SDRAM_OPERATION_CWA_DELAY_SEL_OFFS); + + /* Configure - Issue CWA command with the above parameters */ + reg |= (REG_SDRAM_OPERATION_CMD_CWA & + ~(0xF << REG_SDRAM_OPERATION_CS_OFFS)); + + /* 0x1418 - SDRAM Operation Register */ + dfs_reg_write(REG_SDRAM_OPERATION_ADDR, reg); + + /* Poll - Wait for CWA operation completion */ + do { + reg = reg_read(REG_SDRAM_OPERATION_ADDR) & + (REG_SDRAM_OPERATION_CMD_MASK); + } while (reg); + } + + /* Configure - Exit Self Refresh */ + /* [2] - DfsSR */ + reg = (reg_read(REG_DFS_ADDR) & ~(1 << REG_DFS_SR_OFFS)); + dfs_reg_write(REG_DFS_ADDR, reg); /* 0x1528 - DFS register */ + + /* + * Poll - DFS Register - 0x1528 [3] - DfsAtSR - All DRAM devices + * on all ranks are NOT in self refresh mode + */ + do { + reg = ((reg_read(REG_DFS_ADDR)) & (1 << REG_DFS_ATSR_OFFS)); + } while (reg); /* Wait for '0' */ + + /* Configure - Issue Refresh command */ + /* [3-0] = 0x2 - Refresh Command, [11-8] - enabled Cs */ + reg = REG_SDRAM_OPERATION_CMD_RFRS; + for (cs = 0; cs < MAX_CS; cs++) { + if (dram_info->cs_ena & (1 << cs)) + reg &= ~(1 << (REG_SDRAM_OPERATION_CS_OFFS + cs)); + } + + /* 0x1418 - SDRAM Operation Register */ + dfs_reg_write(REG_SDRAM_OPERATION_ADDR, reg); + + /* Poll - Wait for Refresh operation completion */ + wait_refresh_op_complete(); + + /* Configure - Block new external transactions - Disable */ + reg = reg_read(REG_DFS_ADDR); + reg &= ~(1 << REG_DFS_BLOCK_OFFS); /* [1] - DfsBlock - Disable */ + dfs_reg_write(REG_DFS_ADDR, reg); /* 0x1528 - DFS register */ + + /* + * Configure - XBAR Retry response during Block to enable + * internal access - Disable + */ + reg = reg_read(REG_METAL_MASK_ADDR); + /* [0] - RetryMask - Enable */ + reg |= (1 << REG_METAL_MASK_RETRY_OFFS); + /* 0x14B0 - Dunit MMask Register */ + dfs_reg_write(REG_METAL_MASK_ADDR, reg); + + for (cs = 0; cs < MAX_CS; cs++) { + if (dram_info->cs_ena & (1 << cs)) { + /* Configure - Set CL */ + reg = reg_read(REG_DDR3_MR0_CS_ADDR + + (cs << MR_CS_ADDR_OFFS)) & + ~REG_DDR3_MR0_CL_MASK; + tmp = 0x4; /* CL=6 - 0x4 */ + reg |= ((tmp & 0x1) << REG_DDR3_MR0_CL_OFFS); + reg |= ((tmp & 0xE) << REG_DDR3_MR0_CL_HIGH_OFFS); + dfs_reg_write(REG_DDR3_MR0_CS_ADDR + + (cs << MR_CS_ADDR_OFFS), reg); + + /* Configure - Set CWL */ + reg = reg_read(REG_DDR3_MR2_CS_ADDR + + (cs << MR_CS_ADDR_OFFS)) + & ~(REG_DDR3_MR2_CWL_MASK << REG_DDR3_MR2_CWL_OFFS); + /* CWL=6 - 0x1 */ + reg |= ((0x1) << REG_DDR3_MR2_CWL_OFFS); + dfs_reg_write(REG_DDR3_MR2_CS_ADDR + + (cs << MR_CS_ADDR_OFFS), reg); + } + } + + DEBUG_DFS_C("DDR3 - DFS - High To Low - Ended successfuly - new Frequency - ", + freq, 1); + + return MV_OK; +#else + /* This Flow is relevant for Armada370 A0 and ArmadaXP Z1 */ + + u32 reg, freq_par; + u32 cs = 0; + + DEBUG_DFS_C("DDR3 - DFS - High To Low - Starting DFS procedure to Frequency - ", + freq, 1); + + /* target frequency - 100MHz */ + freq_par = ddr3_get_freq_parameter(freq, 0); + + reg = 0x0000FF00; + /* 0x18700 - CPU Div CLK control 0 */ + dfs_reg_write(REG_CPU_DIV_CLK_CTRL_1_ADDR, reg); + + /* 0x1600 - ODPG_CNTRL_Control */ + reg = reg_read(REG_ODPG_CNTRL_ADDR); + /* [21] = 1 - auto refresh disable */ + reg |= (1 << REG_ODPG_CNTRL_OFFS); + dfs_reg_write(REG_ODPG_CNTRL_ADDR, reg); + + /* 0x1670 - PHY lock mask register */ + reg = reg_read(REG_PHY_LOCK_MASK_ADDR); + reg &= REG_PHY_LOCK_MASK_MASK; /* [11:0] = 0 */ + dfs_reg_write(REG_PHY_LOCK_MASK_ADDR, reg); + + reg = reg_read(REG_DFS_ADDR); /* 0x1528 - DFS register */ + + /* Disable reconfig */ + reg &= ~0x10; /* [4] - Enable reconfig MR registers after DFS_ERG */ + reg |= 0x1; /* [0] - DRAM DLL disabled after DFS */ + + dfs_reg_write(REG_DFS_ADDR, reg); /* 0x1528 - DFS register */ + + reg = reg_read(REG_METAL_MASK_ADDR) & ~(1 << 0); /* [0] - disable */ + /* 0x14B0 - Dunit MMask Register */ + dfs_reg_write(REG_METAL_MASK_ADDR, reg); + + /* [1] - DFS Block enable */ + reg = reg_read(REG_DFS_ADDR) | (1 << REG_DFS_BLOCK_OFFS); + dfs_reg_write(REG_DFS_ADDR, reg); /* 0x1528 - DFS register */ + + /* [2] - DFS Self refresh enable */ + reg = reg_read(REG_DFS_ADDR) | (1 << REG_DFS_SR_OFFS); + dfs_reg_write(REG_DFS_ADDR, reg); /* 0x1528 - DFS register */ + + /* + * Poll DFS Register - 0x1528 [3] - DfsAtSR - + * All DRAM devices on all ranks are in self refresh mode - + * DFS can be executed afterwards + */ + do { + reg = reg_read(REG_DFS_ADDR) & (1 << REG_DFS_ATSR_OFFS); + } while (reg == 0x0); /* Wait for '1' */ + + /* Disable ODT on DLL-off mode */ + dfs_reg_write(REG_SDRAM_ODT_CTRL_HIGH_ADDR, + REG_SDRAM_ODT_CTRL_HIGH_OVRD_MASK); + + /* [11:0] = 0 */ + reg = (reg_read(REG_PHY_LOCK_MASK_ADDR) & REG_PHY_LOCK_MASK_MASK); + /* 0x1670 - PHY lock mask register */ + dfs_reg_write(REG_PHY_LOCK_MASK_ADDR, reg); + + /* Add delay between entering SR and start ratio modification */ + udelay(1); + + /* + * Initial Setup - assure that the "load new ratio" is clear (bit 24) + * and in the same chance, block reassertions of reset [15:8] and + * force reserved bits[7:0]. + */ + reg = 0x0000FDFF; + /* 0x18700 - CPU Div CLK control 0 */ + dfs_reg_write(REG_CPU_DIV_CLK_CTRL_0_ADDR, reg); + + /* + * RelaX whenever reset is asserted to that channel (good for any case) + */ + reg = 0x0000FF00; + /* 0x18700 - CPU Div CLK control 0 */ + dfs_reg_write(REG_CPU_DIV_CLK_CTRL_1_ADDR, reg); + + reg = reg_read(REG_CPU_DIV_CLK_CTRL_3_ADDR) & + REG_CPU_DIV_CLK_CTRL_3_FREQ_MASK; + /* Full Integer ratio from PLL-out to ddr-clk */ + reg |= (freq_par << REG_CPU_DIV_CLK_CTRL_3_FREQ_OFFS); + /* 0x1870C - CPU Div CLK control 3 register */ + dfs_reg_write(REG_CPU_DIV_CLK_CTRL_3_ADDR, reg); + + /* + * Shut off clock enable to the DDRPHY clock channel (this is the "D"). + * All the rest are kept as is (forced, but could be read-modify-write). + * This is done now by RMW above. + */ + + /* Clock is not shut off gracefully - keep it running */ + reg = 0x000FFF02; + dfs_reg_write(REG_CPU_DIV_CLK_CTRL_4_ADDR, reg); + + /* Wait before replacing the clock on the DDR Phy Channel. */ + udelay(1); + + /* + * This for triggering the frequency update. Bit[24] is the + * central control + * bits [23:16] == which channels to change ==2 ==> only DDR Phy + * (smooth transition) + * bits [15:8] == mask reset reassertion due to clock modification + * to these channels. + * bits [7:0] == not in use + */ + reg = 0x0102FDFF; + /* 0x18700 - CPU Div CLK control 0 register */ + dfs_reg_write(REG_CPU_DIV_CLK_CTRL_0_ADDR, reg); + + udelay(1); /* Wait 1usec */ + + /* + * Poll Div CLK status 0 register - indication that the clocks + * are active - 0x18718 [8] + */ + do { + reg = (reg_read(REG_CPU_DIV_CLK_STATUS_0_ADDR)) & + (1 << REG_CPU_DIV_CLK_ALL_STABLE_OFFS); + } while (reg == 0); + + /* + * Clean the CTRL0, to be ready for next resets and next requests of + * ratio modifications. + */ + reg = 0x000000FF; + /* 0x18700 - CPU Div CLK control 0 register */ + dfs_reg_write(REG_CPU_DIV_CLK_CTRL_0_ADDR, reg); + + udelay(5); + + /* Switch HCLK Mux to training clk (100Mhz), keep DFS request bit */ + reg = 0x20050000; + /* 0x18488 - DRAM Init control status register */ + dfs_reg_write(REG_DRAM_INIT_CTRL_STATUS_ADDR, reg); + + reg = reg_read(REG_DDR_IO_ADDR) & ~(1 << REG_DDR_IO_CLK_RATIO_OFFS); + /* [15] = 0 - Set 1:1 Ratio between Dunit and Phy */ + dfs_reg_write(REG_DDR_IO_ADDR, reg); /* 0x1524 - DDR IO Regist */ + + reg = reg_read(REG_DRAM_PHY_CONFIG_ADDR) & REG_DRAM_PHY_CONFIG_MASK; + /* [31:30]] - reset pup data ctrl ADLL */ + /* 0x15EC - DRAM PHY Config register */ + dfs_reg_write(REG_DRAM_PHY_CONFIG_ADDR, reg); + + reg = (reg_read(REG_DRAM_PHY_CONFIG_ADDR) | ~REG_DRAM_PHY_CONFIG_MASK); + /* [31:30] - normal pup data ctrl ADLL */ + /* 0x15EC - DRAM PHY Config register */ + dfs_reg_write(REG_DRAM_PHY_CONFIG_ADDR, reg); + + udelay(1); /* Wait 1usec */ + + /* 0x1404 */ + reg = (reg_read(REG_DUNIT_CTRL_LOW_ADDR) & 0xFFFFFFE7); + dfs_reg_write(REG_DUNIT_CTRL_LOW_ADDR, reg); + + /* Poll Phy lock status register - APLL lock indication - 0x1674 */ + do { + reg = (reg_read(REG_PHY_LOCK_STATUS_ADDR)) & + REG_PHY_LOCK_STATUS_LOCK_MASK; + } while (reg != REG_PHY_LOCK_STATUS_LOCK_MASK); /* Wait for '0xFFF' */ + + reg = (reg_read(REG_SDRAM_CONFIG_ADDR) & REG_SDRAM_CONFIG_MASK); + /* [30:29] = 0 - Data Pup R/W path reset */ + /* 0x1400 - SDRAM Configuration register */ + dfs_reg_write(REG_SDRAM_CONFIG_ADDR, reg); + + reg = reg_read(REG_SDRAM_CONFIG_ADDR) | ~REG_SDRAM_CONFIG_MASK; + /* [30:29] = '11' - Data Pup R/W path reset */ + /* 0x1400 - SDRAM Configuration register */ + dfs_reg_write(REG_SDRAM_CONFIG_ADDR, reg); + + udelay(1000); /* Wait 1msec */ + + for (cs = 0; cs < MAX_CS; cs++) { + if (dram_info->cs_ena & (1 << cs)) { + /* Poll - Wait for Refresh operation completion */ + wait_refresh_op_complete(); + + /* Config CL and CWL with MR0 and MR2 registers */ + reg = reg_read(REG_DDR3_MR0_ADDR); + reg &= ~0x74; /* CL [3:0]; [6:4],[2] */ + reg |= (1 << 5); /* CL = 4, CAS is 6 */ + dfs_reg_write(REG_DDR3_MR0_ADDR, reg); + reg = REG_SDRAM_OPERATION_CMD_MR0 & + ~(1 << (REG_SDRAM_OPERATION_CS_OFFS + cs)); + /* 0x1418 - SDRAM Operation Register */ + dfs_reg_write(REG_SDRAM_OPERATION_ADDR, reg); + + /* Poll - Wait for Refresh operation completion */ + wait_refresh_op_complete(); + + reg = reg_read(REG_DDR3_MR2_ADDR); + reg &= ~0x38; /* CWL [5:3] */ + reg |= (1 << 3); /* CWL = 1, CWL is 6 */ + dfs_reg_write(REG_DDR3_MR2_ADDR, reg); + + reg = REG_SDRAM_OPERATION_CMD_MR2 & + ~(1 << (REG_SDRAM_OPERATION_CS_OFFS + cs)); + /* 0x1418 - SDRAM Operation Register */ + dfs_reg_write(REG_SDRAM_OPERATION_ADDR, reg); + + /* Poll - Wait for Refresh operation completion */ + wait_refresh_op_complete(); + + /* Set current rd_sample_delay */ + reg = reg_read(REG_READ_DATA_SAMPLE_DELAYS_ADDR); + reg &= ~(REG_READ_DATA_SAMPLE_DELAYS_MASK << + (REG_READ_DATA_SAMPLE_DELAYS_OFFS * cs)); + reg |= (5 << (REG_READ_DATA_SAMPLE_DELAYS_OFFS * cs)); + dfs_reg_write(REG_READ_DATA_SAMPLE_DELAYS_ADDR, reg); + + /* Set current rd_ready_delay */ + reg = reg_read(REG_READ_DATA_READY_DELAYS_ADDR); + reg &= ~(REG_READ_DATA_READY_DELAYS_MASK << + (REG_READ_DATA_READY_DELAYS_OFFS * cs)); + reg |= ((6) << (REG_READ_DATA_READY_DELAYS_OFFS * cs)); + dfs_reg_write(REG_READ_DATA_READY_DELAYS_ADDR, reg); + } + } + + /* [2] - DFS Self refresh disable */ + reg = reg_read(REG_DFS_ADDR) & ~(1 << REG_DFS_SR_OFFS); + dfs_reg_write(REG_DFS_ADDR, reg); /* 0x1528 - DFS register */ + + /* [1] - DFS Block enable */ + reg = reg_read(REG_DFS_ADDR) & ~(1 << REG_DFS_BLOCK_OFFS); + dfs_reg_write(REG_DFS_ADDR, reg); /* 0x1528 - DFS register */ + + /* + * Poll DFS Register - 0x1528 [3] - DfsAtSR - + * All DRAM devices on all ranks are in self refresh mode - DFS can + * be executed afterwards + */ + do { + reg = reg_read(REG_DFS_ADDR) & (1 << REG_DFS_ATSR_OFFS); + } while (reg); /* Wait for '1' */ + + reg = (reg_read(REG_METAL_MASK_ADDR) | (1 << 0)); + /* [0] - Enable Dunit to crossbar retry */ + /* 0x14B0 - Dunit MMask Register */ + dfs_reg_write(REG_METAL_MASK_ADDR, reg); + + /* 0x1600 - PHY lock mask register */ + reg = reg_read(REG_ODPG_CNTRL_ADDR); + reg &= ~(1 << REG_ODPG_CNTRL_OFFS); /* [21] = 0 */ + dfs_reg_write(REG_ODPG_CNTRL_ADDR, reg); + + /* 0x1670 - PHY lock mask register */ + reg = reg_read(REG_PHY_LOCK_MASK_ADDR); + reg |= ~REG_PHY_LOCK_MASK_MASK; /* [11:0] = FFF */ + dfs_reg_write(REG_PHY_LOCK_MASK_ADDR, reg); + + DEBUG_DFS_C("DDR3 - DFS - High To Low - Ended successfuly - new Frequency - ", + freq, 1); + + return MV_OK; +#endif +} + +/* + * Name: ddr3_dfs_low_2_high + * Desc: + * Args: freq - target frequency + * Notes: + * Returns: MV_OK - success, MV_FAIL - fail + */ +int ddr3_dfs_low_2_high(u32 freq, int ratio_2to1, MV_DRAM_INFO *dram_info) +{ +#if defined(MV88F78X60) || defined(MV88F672X) + /* This Flow is relevant for ArmadaXP A0 */ + u32 reg, freq_par, tmp; + u32 cs = 0; + + DEBUG_DFS_C("DDR3 - DFS - Low To High - Starting DFS procedure to Frequency - ", + freq, 1); + + /* target frequency - freq */ + freq_par = ddr3_get_freq_parameter(freq, ratio_2to1); + +#if defined(MV88F672X) + u32 hclk; + u32 cpu_freq = ddr3_get_cpu_freq(); + get_target_freq(cpu_freq, &tmp, &hclk); +#endif + + /* Configure - DRAM DLL final state after DFS is complete - Enable */ + reg = reg_read(REG_DFS_ADDR); + /* [0] - DfsDllNextState - Enable */ + reg &= ~(1 << REG_DFS_DLLNEXTSTATE_OFFS); + dfs_reg_write(REG_DFS_ADDR, reg); /* 0x1528 - DFS register */ + + /* + * Configure - XBAR Retry response during Block to enable + * internal access - Disable + */ + reg = reg_read(REG_METAL_MASK_ADDR); + /* [0] - RetryMask - Disable */ + reg &= ~(1 << REG_METAL_MASK_RETRY_OFFS); + /* 0x14B0 - Dunit MMask Register */ + dfs_reg_write(REG_METAL_MASK_ADDR, reg); + + /* Configure - Block new external transactions - Enable */ + reg = reg_read(REG_DFS_ADDR); + reg |= (1 << REG_DFS_BLOCK_OFFS); /* [1] - DfsBlock - Enable */ + dfs_reg_write(REG_DFS_ADDR, reg); /* 0x1528 - DFS register */ + + /* Configure - Move DRAM into Self Refresh */ + reg = reg_read(REG_DFS_ADDR); + reg |= (1 << REG_DFS_SR_OFFS); /* [2] - DfsSR - Enable */ + dfs_reg_write(REG_DFS_ADDR, reg); /* 0x1528 - DFS register */ + + /* Poll - Wait for Self Refresh indication */ + do { + reg = ((reg_read(REG_DFS_ADDR)) & (1 << REG_DFS_ATSR_OFFS)); + } while (reg == 0x0); /* 0x1528 [3] - DfsAtSR - Wait for '1' */ + + /* Start of clock change procedure (PLL) */ +#if defined(MV88F672X) + /* avantaLP */ + /* Configure cpupll_clkdiv_reset_mask */ + reg = reg_read(CPU_PLL_CLOCK_DIVIDER_CNTRL0); + reg &= CPU_PLL_CLOCK_DIVIDER_CNTRL0_MASK; + /* 0xE8264[7:0] 0xff CPU Clock Dividers Reset mask */ + dfs_reg_write(CPU_PLL_CLOCK_DIVIDER_CNTRL0, (reg + 0xFF)); + + /* Configure cpu_clkdiv_reload_smooth */ + reg = reg_read(CPU_PLL_CNTRL0); + reg &= CPU_PLL_CNTRL0_RELOAD_SMOOTH_MASK; + /* 0xE8260 [15:8] 0x2 CPU Clock Dividers Reload Smooth enable */ + dfs_reg_write(CPU_PLL_CNTRL0, + reg + (2 << CPU_PLL_CNTRL0_RELOAD_SMOOTH_OFFS)); + + /* Configure cpupll_clkdiv_relax_en */ + reg = reg_read(CPU_PLL_CNTRL0); + reg &= CPU_PLL_CNTRL0_RELAX_EN_MASK; + /* 0xE8260 [31:24] 0x2 Relax Enable */ + dfs_reg_write(CPU_PLL_CNTRL0, + reg + (2 << CPU_PLL_CNTRL0_RELAX_EN_OFFS)); + + /* Configure cpupll_clkdiv_ddr_clk_ratio */ + reg = reg_read(CPU_PLL_CLOCK_DIVIDER_CNTRL1); + /* + * 0xE8268 [13:8] N Set Training clock: + * APLL Out Clock (VCO freq) / N = 100 MHz + */ + reg &= CPU_PLL_CLOCK_DIVIDER_CNTRL1_MASK; + reg |= (freq_par << 8); /* full Integer ratio from PLL-out to ddr-clk */ + dfs_reg_write(CPU_PLL_CLOCK_DIVIDER_CNTRL1, reg); + /* Configure cpupll_clkdiv_reload_ratio */ + reg = reg_read(CPU_PLL_CLOCK_DIVIDER_CNTRL0); + reg &= CPU_PLL_CLOCK_RELOAD_RATIO_MASK; + /* 0xE8264 [8]=0x1 CPU Clock Dividers Reload Ratio trigger set */ + dfs_reg_write(CPU_PLL_CLOCK_DIVIDER_CNTRL0, + reg + (1 << CPU_PLL_CLOCK_RELOAD_RATIO_OFFS)); + + udelay(1); + + /* Configure cpupll_clkdiv_reload_ratio */ + reg = reg_read(CPU_PLL_CLOCK_DIVIDER_CNTRL0); + reg &= CPU_PLL_CLOCK_RELOAD_RATIO_MASK; + /* 0xE8264 [8]=0x0 CPU Clock Dividers Reload Ratio trigger clear */ + dfs_reg_write(CPU_PLL_CLOCK_DIVIDER_CNTRL0, reg); + + udelay(5); + +#else + /* + * Initial Setup - assure that the "load new ratio" is clear (bit 24) + * and in the same chance, block reassertions of reset [15:8] + * and force reserved bits[7:0]. + */ + reg = 0x0000FFFF; + + /* 0x18700 - CPU Div CLK control 0 */ + dfs_reg_write(REG_CPU_DIV_CLK_CTRL_0_ADDR, reg); + + /* + * RelaX whenever reset is asserted to that channel (good for any case) + */ + reg = 0x0000FF00; + /* 0x18704 - CPU Div CLK control 0 */ + dfs_reg_write(REG_CPU_DIV_CLK_CTRL_1_ADDR, reg); + + reg = reg_read(REG_CPU_DIV_CLK_CTRL_2_ADDR) & + REG_CPU_DIV_CLK_CTRL_3_FREQ_MASK; + reg |= (freq_par << REG_CPU_DIV_CLK_CTRL_3_FREQ_OFFS); + /* full Integer ratio from PLL-out to ddr-clk */ + /* 0x1870C - CPU Div CLK control 3 register */ + dfs_reg_write(REG_CPU_DIV_CLK_CTRL_2_ADDR, reg); + + /* + * Shut off clock enable to the DDRPHY clock channel (this is the "D"). + * All the rest are kept as is (forced, but could be read-modify-write). + * This is done now by RMW above. + */ + reg = 0x000FFF02; + dfs_reg_write(REG_CPU_DIV_CLK_CTRL_4_ADDR, reg); + + /* Wait before replacing the clock on the DDR Phy Channel. */ + udelay(1); + + reg = 0x0102FDFF; + /* + * This for triggering the frequency update. Bit[24] is the + * central control + * bits [23:16] == which channels to change ==2 ==> only DDR Phy + * (smooth transition) + * bits [15:8] == mask reset reassertion due to clock modification + * to these channels. + * bits [7:0] == not in use + */ + /* 0x18700 - CPU Div CLK control 0 register */ + dfs_reg_write(REG_CPU_DIV_CLK_CTRL_0_ADDR, reg); + + udelay(1); + + /* + * Poll Div CLK status 0 register - indication that the clocks + * are active - 0x18718 [8] + */ + do { + reg = reg_read(REG_CPU_DIV_CLK_STATUS_0_ADDR) & + (1 << REG_CPU_DIV_CLK_ALL_STABLE_OFFS); + } while (reg == 0); + + reg = 0x000000FF; + /* + * Clean the CTRL0, to be ready for next resets and next requests + * of ratio modifications. + */ + /* 0x18700 - CPU Div CLK control 0 register */ + dfs_reg_write(REG_CPU_DIV_CLK_CTRL_0_ADDR, reg); +#endif + /* End of clock change procedure (PLL) */ + + if (ratio_2to1) { + /* Configure - Select normal clock for the DDR PHY - Disable */ + reg = reg_read(REG_DRAM_INIT_CTRL_STATUS_ADDR); + /* [16] - ddr_phy_trn_clk_sel - Disable */ + reg &= ~(1 << REG_DRAM_INIT_CTRL_TRN_CLK_OFFS); + /* 0x18488 - DRAM Init control status register */ + dfs_reg_write(REG_DRAM_INIT_CTRL_STATUS_ADDR, reg); + } + + /* + * Configure - Set Correct Ratio - according to target ratio + * parameter - 2:1/1:1 + */ + if (ratio_2to1) { + /* + * [15] - Phy2UnitClkRatio = 1 - Set 2:1 Ratio between + * Dunit and Phy + */ + reg = reg_read(REG_DDR_IO_ADDR) | + (1 << REG_DDR_IO_CLK_RATIO_OFFS); + } else { + /* + * [15] - Phy2UnitClkRatio = 0 - Set 1:1 Ratio between + * Dunit and Phy + */ + reg = reg_read(REG_DDR_IO_ADDR) & + ~(1 << REG_DDR_IO_CLK_RATIO_OFFS); + } + dfs_reg_write(REG_DDR_IO_ADDR, reg); /* 0x1524 - DDR IO Register */ + + /* Configure - 2T Mode - Restore original configuration */ + reg = reg_read(REG_DUNIT_CTRL_LOW_ADDR); + /* [3:4] 2T - Restore value */ + reg &= ~(REG_DUNIT_CTRL_LOW_2T_MASK << REG_DUNIT_CTRL_LOW_2T_OFFS); + reg |= ((dram_info->mode_2t & REG_DUNIT_CTRL_LOW_2T_MASK) << + REG_DUNIT_CTRL_LOW_2T_OFFS); + /* 0x1404 - DDR Controller Control Low Register */ + dfs_reg_write(REG_DUNIT_CTRL_LOW_ADDR, reg); + + /* Configure - Restore CL and CWL - MRS Commands */ + reg = reg_read(REG_DFS_ADDR); + reg &= ~(REG_DFS_CL_NEXT_STATE_MASK << REG_DFS_CL_NEXT_STATE_OFFS); + reg &= ~(REG_DFS_CWL_NEXT_STATE_MASK << REG_DFS_CWL_NEXT_STATE_OFFS); + + if (freq == DDR_400) { + if (dram_info->target_frequency == 0x8) + tmp = ddr3_cl_to_valid_cl(5); + else + tmp = ddr3_cl_to_valid_cl(6); + } else { + tmp = ddr3_cl_to_valid_cl(dram_info->cl); + } + + /* [8] - DfsCLNextState */ + reg |= ((tmp & REG_DFS_CL_NEXT_STATE_MASK) << REG_DFS_CL_NEXT_STATE_OFFS); + if (freq == DDR_400) { + /* [12] - DfsCWLNextState */ + reg |= (((0) & REG_DFS_CWL_NEXT_STATE_MASK) << + REG_DFS_CWL_NEXT_STATE_OFFS); + } else { + /* [12] - DfsCWLNextState */ + reg |= (((dram_info->cwl) & REG_DFS_CWL_NEXT_STATE_MASK) << + REG_DFS_CWL_NEXT_STATE_OFFS); + } + dfs_reg_write(REG_DFS_ADDR, reg); /* 0x1528 - DFS register */ + + /* Optional - Configure - DDR3_Rtt_nom_CS# */ + for (cs = 0; cs < MAX_CS; cs++) { + if (dram_info->cs_ena & (1 << cs)) { + reg = reg_read(REG_DDR3_MR1_CS_ADDR + + (cs << MR_CS_ADDR_OFFS)); + reg &= REG_DDR3_MR1_RTT_MASK; + reg |= odt_static[dram_info->cs_ena][cs]; + dfs_reg_write(REG_DDR3_MR1_CS_ADDR + + (cs << MR_CS_ADDR_OFFS), reg); + } + } + + /* Configure - Reset ADLLs - Set Reset */ + reg = reg_read(REG_DRAM_PHY_CONFIG_ADDR) & REG_DRAM_PHY_CONFIG_MASK; + /* [31:30]] - reset pup data ctrl ADLL */ + /* 0x15EC - DRAM PHY Config Register */ + dfs_reg_write(REG_DRAM_PHY_CONFIG_ADDR, reg); + + /* Configure - Reset ADLLs - Release Reset */ + reg = reg_read(REG_DRAM_PHY_CONFIG_ADDR) | ~REG_DRAM_PHY_CONFIG_MASK; + /* [31:30] - normal pup data ctrl ADLL */ + /* 0x15EC - DRAM PHY Config register */ + dfs_reg_write(REG_DRAM_PHY_CONFIG_ADDR, reg); + + /* Poll - Wait for APLL + ADLLs lock on new frequency */ + do { + reg = reg_read(REG_PHY_LOCK_STATUS_ADDR) & + REG_PHY_LOCK_APLL_ADLL_STATUS_MASK; + /* 0x1674 [10:0] - Phy lock status Register */ + } while (reg != REG_PHY_LOCK_APLL_ADLL_STATUS_MASK); + + /* Configure - Reset the PHY SDR clock divider */ + if (ratio_2to1) { + /* Pup Reset Divider B - Set Reset */ + /* [28] - DataPupRdRST = 0 */ + reg = reg_read(REG_SDRAM_CONFIG_ADDR) & + ~(1 << REG_SDRAM_CONFIG_PUPRSTDIV_OFFS); + /* [28] - DataPupRdRST = 1 */ + tmp = reg_read(REG_SDRAM_CONFIG_ADDR) | + (1 << REG_SDRAM_CONFIG_PUPRSTDIV_OFFS); + /* 0x1400 - SDRAM Configuration register */ + dfs_reg_write(REG_SDRAM_CONFIG_ADDR, reg); + + /* Pup Reset Divider B - Release Reset */ + /* 0x1400 - SDRAM Configuration register */ + dfs_reg_write(REG_SDRAM_CONFIG_ADDR, tmp); + } + + /* Configure - Reset the PHY Read FIFO and Write channels - Set Reset */ + reg = reg_read(REG_SDRAM_CONFIG_ADDR) & REG_SDRAM_CONFIG_MASK; + /* [30:29] = 0 - Data Pup R/W path reset */ + /* 0x1400 - SDRAM Configuration register */ + dfs_reg_write(REG_SDRAM_CONFIG_ADDR, reg); + + /* + * Configure - DRAM Data PHY Read [30], Write [29] path reset - + * Release Reset + */ + reg = reg_read(REG_SDRAM_CONFIG_ADDR) | ~REG_SDRAM_CONFIG_MASK; + /* [30:29] = '11' - Data Pup R/W path reset */ + /* 0x1400 - SDRAM Configuration register */ + dfs_reg_write(REG_SDRAM_CONFIG_ADDR, reg); + + /* Registered DIMM support */ + if (dram_info->reg_dimm) { + /* + * Configure - Change register DRAM operating speed + * (DDR3-1333 / DDR3-1600) - CWA_RC + */ + reg = (0xA & REG_SDRAM_OPERATION_CWA_RC_MASK) << + REG_SDRAM_OPERATION_CWA_RC_OFFS; + if (freq <= DDR_400) { + /* + * Configure - Change register DRAM operating speed + * (DDR3-800) - CWA_DATA + */ + reg |= ((0x0 & REG_SDRAM_OPERATION_CWA_DATA_MASK) << + REG_SDRAM_OPERATION_CWA_DATA_OFFS); + } else if ((freq > DDR_400) && (freq <= DDR_533)) { + /* + * Configure - Change register DRAM operating speed + * (DDR3-1066) - CWA_DATA + */ + reg |= ((0x1 & REG_SDRAM_OPERATION_CWA_DATA_MASK) << + REG_SDRAM_OPERATION_CWA_DATA_OFFS); + } else if ((freq > DDR_533) && (freq <= DDR_666)) { + /* + * Configure - Change register DRAM operating speed + * (DDR3-1333) - CWA_DATA + */ + reg |= ((0x2 & REG_SDRAM_OPERATION_CWA_DATA_MASK) << + REG_SDRAM_OPERATION_CWA_DATA_OFFS); + } else { + /* + * Configure - Change register DRAM operating speed + * (DDR3-1600) - CWA_DATA + */ + reg |= ((0x3 & REG_SDRAM_OPERATION_CWA_DATA_MASK) << + REG_SDRAM_OPERATION_CWA_DATA_OFFS); + } + + /* Configure - Set Delay - tSTAB */ + reg |= (0x1 << REG_SDRAM_OPERATION_CWA_DELAY_SEL_OFFS); + /* Configure - Issue CWA command with the above parameters */ + reg |= (REG_SDRAM_OPERATION_CMD_CWA & + ~(0xF << REG_SDRAM_OPERATION_CS_OFFS)); + + /* 0x1418 - SDRAM Operation Register */ + dfs_reg_write(REG_SDRAM_OPERATION_ADDR, reg); + + /* Poll - Wait for CWA operation completion */ + do { + reg = reg_read(REG_SDRAM_OPERATION_ADDR) & + REG_SDRAM_OPERATION_CMD_MASK; + } while (reg); + } + + /* Configure - Exit Self Refresh */ + /* [2] - DfsSR */ + reg = reg_read(REG_DFS_ADDR) & ~(1 << REG_DFS_SR_OFFS); + dfs_reg_write(REG_DFS_ADDR, reg); /* 0x1528 - DFS register */ + + /* + * Poll - DFS Register - 0x1528 [3] - DfsAtSR - All DRAM + * devices on all ranks are NOT in self refresh mode + */ + do { + reg = reg_read(REG_DFS_ADDR) & (1 << REG_DFS_ATSR_OFFS); + } while (reg); /* Wait for '0' */ + + /* Configure - Issue Refresh command */ + /* [3-0] = 0x2 - Refresh Command, [11-8] - enabled Cs */ + reg = REG_SDRAM_OPERATION_CMD_RFRS; + for (cs = 0; cs < MAX_CS; cs++) { + if (dram_info->cs_ena & (1 << cs)) + reg &= ~(1 << (REG_SDRAM_OPERATION_CS_OFFS + cs)); + } + + /* 0x1418 - SDRAM Operation Register */ + dfs_reg_write(REG_SDRAM_OPERATION_ADDR, reg); + + /* Poll - Wait for Refresh operation completion */ + wait_refresh_op_complete(); + + /* Configure - Block new external transactions - Disable */ + reg = reg_read(REG_DFS_ADDR); + reg &= ~(1 << REG_DFS_BLOCK_OFFS); /* [1] - DfsBlock - Disable */ + dfs_reg_write(REG_DFS_ADDR, reg); /* 0x1528 - DFS register */ + + /* + * Configure - XBAR Retry response during Block to enable + * internal access - Disable + */ + reg = reg_read(REG_METAL_MASK_ADDR); + /* [0] - RetryMask - Enable */ + reg |= (1 << REG_METAL_MASK_RETRY_OFFS); + /* 0x14B0 - Dunit MMask Register */ + dfs_reg_write(REG_METAL_MASK_ADDR, reg); + + for (cs = 0; cs < MAX_CS; cs++) { + if (dram_info->cs_ena & (1 << cs)) { + /* Configure - Set CL */ + reg = reg_read(REG_DDR3_MR0_CS_ADDR + + (cs << MR_CS_ADDR_OFFS)) & + ~REG_DDR3_MR0_CL_MASK; + if (freq == DDR_400) + tmp = ddr3_cl_to_valid_cl(6); + else + tmp = ddr3_cl_to_valid_cl(dram_info->cl); + reg |= ((tmp & 0x1) << REG_DDR3_MR0_CL_OFFS); + reg |= ((tmp & 0xE) << REG_DDR3_MR0_CL_HIGH_OFFS); + dfs_reg_write(REG_DDR3_MR0_CS_ADDR + + (cs << MR_CS_ADDR_OFFS), reg); + + /* Configure - Set CWL */ + reg = reg_read(REG_DDR3_MR2_CS_ADDR + + (cs << MR_CS_ADDR_OFFS)) & + ~(REG_DDR3_MR2_CWL_MASK << REG_DDR3_MR2_CWL_OFFS); + if (freq == DDR_400) + reg |= ((0) << REG_DDR3_MR2_CWL_OFFS); + else + reg |= ((dram_info->cwl) << REG_DDR3_MR2_CWL_OFFS); + dfs_reg_write(REG_DDR3_MR2_CS_ADDR + + (cs << MR_CS_ADDR_OFFS), reg); + } + } + + DEBUG_DFS_C("DDR3 - DFS - Low To High - Ended successfuly - new Frequency - ", + freq, 1); + + return MV_OK; + +#else + + /* This Flow is relevant for Armada370 A0 and ArmadaXP Z1 */ + + u32 reg, freq_par, tmp; + u32 cs = 0; + + DEBUG_DFS_C("DDR3 - DFS - Low To High - Starting DFS procedure to Frequency - ", + freq, 1); + + /* target frequency - freq */ + freq_par = ddr3_get_freq_parameter(freq, ratio_2to1); + + reg = 0x0000FF00; + dfs_reg_write(REG_CPU_DIV_CLK_CTRL_1_ADDR, reg); + + /* 0x1600 - PHY lock mask register */ + reg = reg_read(REG_ODPG_CNTRL_ADDR); + reg |= (1 << REG_ODPG_CNTRL_OFFS); /* [21] = 1 */ + dfs_reg_write(REG_ODPG_CNTRL_ADDR, reg); + + /* 0x1670 - PHY lock mask register */ + reg = reg_read(REG_PHY_LOCK_MASK_ADDR); + reg &= REG_PHY_LOCK_MASK_MASK; /* [11:0] = 0 */ + dfs_reg_write(REG_PHY_LOCK_MASK_ADDR, reg); + + /* Enable reconfig MR Registers after DFS */ + reg = reg_read(REG_DFS_ADDR); /* 0x1528 - DFS register */ + /* [4] - Disable - reconfig MR registers after DFS_ERG */ + reg &= ~0x11; + /* [0] - Enable - DRAM DLL after DFS */ + dfs_reg_write(REG_DFS_ADDR, reg); /* 0x1528 - DFS register */ + + /* Disable DRAM Controller to crossbar retry */ + /* [0] - disable */ + reg = reg_read(REG_METAL_MASK_ADDR) & ~(1 << 0); + /* 0x14B0 - Dunit MMask Register */ + dfs_reg_write(REG_METAL_MASK_ADDR, reg); + + /* Enable DRAM Blocking */ + /* [1] - DFS Block enable */ + reg = reg_read(REG_DFS_ADDR) | (1 << REG_DFS_BLOCK_OFFS); + dfs_reg_write(REG_DFS_ADDR, reg); /* 0x1528 - DFS register */ + + /* Enable Self refresh */ + /* [2] - DFS Self refresh enable */ + reg = reg_read(REG_DFS_ADDR) | (1 << REG_DFS_SR_OFFS); + dfs_reg_write(REG_DFS_ADDR, reg); /* 0x1528 - DFS register */ + + /* + * Poll DFS Register - All DRAM devices on all ranks are in + * self refresh mode - DFS can be executed afterwards + */ + /* 0x1528 [3] - DfsAtSR */ + do { + reg = reg_read(REG_DFS_ADDR) & (1 << REG_DFS_ATSR_OFFS); + } while (reg == 0x0); /* Wait for '1' */ + + /* + * Set Correct Ratio - if freq>MARGIN_FREQ use 2:1 ratio + * else use 1:1 ratio + */ + if (ratio_2to1) { + /* [15] = 1 - Set 2:1 Ratio between Dunit and Phy */ + reg = reg_read(REG_DDR_IO_ADDR) | + (1 << REG_DDR_IO_CLK_RATIO_OFFS); + } else { + /* [15] = 0 - Set 1:1 Ratio between Dunit and Phy */ + reg = reg_read(REG_DDR_IO_ADDR) & + ~(1 << REG_DDR_IO_CLK_RATIO_OFFS); + } + dfs_reg_write(REG_DDR_IO_ADDR, reg); /* 0x1524 - DDR IO Register */ + + /* Switch HCLK Mux from (100Mhz) [16]=0, keep DFS request bit */ + reg = 0x20040000; + /* + * [29] - training logic request DFS, [28:27] - + * preload patterns frequency [18] + */ + + /* 0x18488 - DRAM Init control status register */ + dfs_reg_write(REG_DRAM_INIT_CTRL_STATUS_ADDR, reg); + + /* Add delay between entering SR and start ratio modification */ + udelay(1); + + /* + * Initial Setup - assure that the "load new ratio" is clear (bit 24) + * and in the same chance, block reassertions of reset [15:8] and + * force reserved bits[7:0]. + */ + reg = 0x0000FFFF; + /* 0x18700 - CPU Div CLK control 0 */ + dfs_reg_write(REG_CPU_DIV_CLK_CTRL_0_ADDR, reg); + + /* + * RelaX whenever reset is asserted to that channel (good for any case) + */ + reg = 0x0000FF00; + /* 0x18704 - CPU Div CLK control 0 */ + dfs_reg_write(REG_CPU_DIV_CLK_CTRL_1_ADDR, reg); + + reg = reg_read(REG_CPU_DIV_CLK_CTRL_3_ADDR) & + REG_CPU_DIV_CLK_CTRL_3_FREQ_MASK; + reg |= (freq_par << REG_CPU_DIV_CLK_CTRL_3_FREQ_OFFS); + /* Full Integer ratio from PLL-out to ddr-clk */ + /* 0x1870C - CPU Div CLK control 3 register */ + dfs_reg_write(REG_CPU_DIV_CLK_CTRL_3_ADDR, reg); + + /* + * Shut off clock enable to the DDRPHY clock channel (this is the "D"). + * All the rest are kept as is (forced, but could be read-modify-write). + * This is done now by RMW above. + */ + + reg = 0x000FFF02; + + dfs_reg_write(REG_CPU_DIV_CLK_CTRL_4_ADDR, reg); + + /* Wait before replacing the clock on the DDR Phy Channel. */ + udelay(1); + + reg = 0x0102FDFF; + /* + * This for triggering the frequency update. Bit[24] is the + * central control + * bits [23:16] == which channels to change ==2 ==> only DDR Phy + * (smooth transition) + * bits [15:8] == mask reset reassertion due to clock modification + * to these channels. + * bits [7:0] == not in use + */ + /* 0x18700 - CPU Div CLK control 0 register */ + dfs_reg_write(REG_CPU_DIV_CLK_CTRL_0_ADDR, reg); + + udelay(1); + + /* + * Poll Div CLK status 0 register - indication that the clocks are + * active - 0x18718 [8] + */ + do { + reg = reg_read(REG_CPU_DIV_CLK_STATUS_0_ADDR) & + (1 << REG_CPU_DIV_CLK_ALL_STABLE_OFFS); + } while (reg == 0); + + reg = 0x000000FF; + /* + * Clean the CTRL0, to be ready for next resets and next requests of + * ratio modifications. + */ + /* 0x18700 - CPU Div CLK control 0 register */ + dfs_reg_write(REG_CPU_DIV_CLK_CTRL_0_ADDR, reg); + + udelay(5); + + if (ratio_2to1) { + /* Pup Reset Divider B - Set Reset */ + /* [28] = 0 - Pup Reset Divider B */ + reg = reg_read(REG_SDRAM_CONFIG_ADDR) & ~(1 << 28); + /* [28] = 1 - Pup Reset Divider B */ + tmp = reg_read(REG_SDRAM_CONFIG_ADDR) | (1 << 28); + /* 0x1400 - SDRAM Configuration register */ + dfs_reg_write(REG_SDRAM_CONFIG_ADDR, reg); + + /* Pup Reset Divider B - Release Reset */ + /* 0x1400 - SDRAM Configuration register */ + dfs_reg_write(REG_SDRAM_CONFIG_ADDR, tmp); + } + + /* DRAM Data PHYs ADLL Reset - Set Reset */ + reg = (reg_read(REG_DRAM_PHY_CONFIG_ADDR) & REG_DRAM_PHY_CONFIG_MASK); + /* [31:30]] - reset pup data ctrl ADLL */ + /* 0x15EC - DRAM PHY Config Register */ + dfs_reg_write(REG_DRAM_PHY_CONFIG_ADDR, reg); + + udelay(25); + + /* APLL lock indication - Poll Phy lock status Register - 0x1674 [9] */ + do { + reg = reg_read(REG_PHY_LOCK_STATUS_ADDR) & + (1 << REG_PHY_LOCK_STATUS_LOCK_OFFS); + } while (reg == 0); + + /* DRAM Data PHYs ADLL Reset - Release Reset */ + reg = reg_read(REG_DRAM_PHY_CONFIG_ADDR) | ~REG_DRAM_PHY_CONFIG_MASK; + /* [31:30] - normal pup data ctrl ADLL */ + /* 0x15EC - DRAM PHY Config register */ + dfs_reg_write(REG_DRAM_PHY_CONFIG_ADDR, reg); + + udelay(10000); /* Wait 10msec */ + + /* + * APLL lock indication - Poll Phy lock status Register - 0x1674 [11:0] + */ + do { + reg = reg_read(REG_PHY_LOCK_STATUS_ADDR) & + REG_PHY_LOCK_STATUS_LOCK_MASK; + } while (reg != REG_PHY_LOCK_STATUS_LOCK_MASK); + + /* DRAM Data PHY Read [30], Write [29] path reset - Set Reset */ + reg = reg_read(REG_SDRAM_CONFIG_ADDR) & REG_SDRAM_CONFIG_MASK; + /* [30:29] = 0 - Data Pup R/W path reset */ + /* 0x1400 - SDRAM Configuration register */ + dfs_reg_write(REG_SDRAM_CONFIG_ADDR, reg); + + /* DRAM Data PHY Read [30], Write [29] path reset - Release Reset */ + reg = reg_read(REG_SDRAM_CONFIG_ADDR) | ~REG_SDRAM_CONFIG_MASK; + /* [30:29] = '11' - Data Pup R/W path reset */ + /* 0x1400 - SDRAM Configuration register */ + dfs_reg_write(REG_SDRAM_CONFIG_ADDR, reg); + + /* Disable DFS Reconfig */ + reg = reg_read(REG_DFS_ADDR) & ~(1 << 4); + dfs_reg_write(REG_DFS_ADDR, reg); /* 0x1528 - DFS register */ + + /* [2] - DFS Self refresh disable */ + reg = reg_read(REG_DFS_ADDR) & ~(1 << REG_DFS_SR_OFFS); + dfs_reg_write(REG_DFS_ADDR, reg); /* 0x1528 - DFS register */ + + /* + * Poll DFS Register - 0x1528 [3] - DfsAtSR - All DRAM devices on + * all ranks are NOT in self refresh mode + */ + do { + reg = reg_read(REG_DFS_ADDR) & (1 << REG_DFS_ATSR_OFFS); + } while (reg); /* Wait for '0' */ + + /* 0x1404 */ + reg = (reg_read(REG_DUNIT_CTRL_LOW_ADDR) & 0xFFFFFFE7) | 0x2; + + /* Configure - 2T Mode - Restore original configuration */ + /* [3:4] 2T - Restore value */ + reg &= ~(REG_DUNIT_CTRL_LOW_2T_MASK << REG_DUNIT_CTRL_LOW_2T_OFFS); + reg |= ((dram_info->mode_2t & REG_DUNIT_CTRL_LOW_2T_MASK) << + REG_DUNIT_CTRL_LOW_2T_OFFS); + dfs_reg_write(REG_DUNIT_CTRL_LOW_ADDR, reg); + + udelay(1); /* Wait 1us */ + + for (cs = 0; cs < MAX_CS; cs++) { + if (dram_info->cs_ena & (1 << cs)) { + reg = (reg_read(REG_DDR3_MR1_ADDR)); + /* DLL Enable */ + reg &= ~(1 << REG_DDR3_MR1_DLL_ENA_OFFS); + dfs_reg_write(REG_DDR3_MR1_ADDR, reg); + + /* Issue MRS Command to current cs */ + reg = REG_SDRAM_OPERATION_CMD_MR1 & + ~(1 << (REG_SDRAM_OPERATION_CS_OFFS + cs)); + /* + * [3-0] = 0x4 - MR1 Command, [11-8] - + * enable current cs + */ + /* 0x1418 - SDRAM Operation Register */ + dfs_reg_write(REG_SDRAM_OPERATION_ADDR, reg); + + /* Poll - Wait for Refresh operation completion */ + wait_refresh_op_complete(); + + /* DLL Reset - MR0 */ + reg = reg_read(REG_DDR3_MR0_ADDR); + dfs_reg_write(REG_DDR3_MR0_ADDR, reg); + + /* Issue MRS Command to current cs */ + reg = REG_SDRAM_OPERATION_CMD_MR0 & + ~(1 << (REG_SDRAM_OPERATION_CS_OFFS + cs)); + /* + * [3-0] = 0x4 - MR1 Command, [11-8] - + * enable current cs + */ + /* 0x1418 - SDRAM Operation Register */ + dfs_reg_write(REG_SDRAM_OPERATION_ADDR, reg); + + /* Poll - Wait for Refresh operation completion */ + wait_refresh_op_complete(); + + reg = reg_read(REG_DDR3_MR0_ADDR); + reg &= ~0x74; /* CL [3:0]; [6:4],[2] */ + + if (freq == DDR_400) + tmp = ddr3_cl_to_valid_cl(6) & 0xF; + else + tmp = ddr3_cl_to_valid_cl(dram_info->cl) & 0xF; + + reg |= ((tmp & 0x1) << 2); + reg |= ((tmp >> 1) << 4); /* to bit 4 */ + dfs_reg_write(REG_DDR3_MR0_ADDR, reg); + + reg = REG_SDRAM_OPERATION_CMD_MR0 & + ~(1 << (REG_SDRAM_OPERATION_CS_OFFS + cs)); + /* 0x1418 - SDRAM Operation Register */ + dfs_reg_write(REG_SDRAM_OPERATION_ADDR, reg); + + /* Poll - Wait for Refresh operation completion */ + wait_refresh_op_complete(); + + reg = reg_read(REG_DDR3_MR2_ADDR); + reg &= ~0x38; /* CWL [5:3] */ + /* CWL = 0 ,for 400 MHg is 5 */ + if (freq != DDR_400) + reg |= dram_info->cwl << REG_DDR3_MR2_CWL_OFFS; + dfs_reg_write(REG_DDR3_MR2_ADDR, reg); + reg = REG_SDRAM_OPERATION_CMD_MR2 & + ~(1 << (REG_SDRAM_OPERATION_CS_OFFS + cs)); + /* 0x1418 - SDRAM Operation Register */ + dfs_reg_write(REG_SDRAM_OPERATION_ADDR, reg); + + /* Poll - Wait for Refresh operation completion */ + wait_refresh_op_complete(); + + /* Set current rd_sample_delay */ + reg = reg_read(REG_READ_DATA_SAMPLE_DELAYS_ADDR); + reg &= ~(REG_READ_DATA_SAMPLE_DELAYS_MASK << + (REG_READ_DATA_SAMPLE_DELAYS_OFFS * cs)); + reg |= (dram_info->cl << + (REG_READ_DATA_SAMPLE_DELAYS_OFFS * cs)); + dfs_reg_write(REG_READ_DATA_SAMPLE_DELAYS_ADDR, reg); + + /* Set current rd_ready_delay */ + reg = reg_read(REG_READ_DATA_READY_DELAYS_ADDR); + reg &= ~(REG_READ_DATA_READY_DELAYS_MASK << + (REG_READ_DATA_READY_DELAYS_OFFS * cs)); + reg |= ((dram_info->cl + 1) << + (REG_READ_DATA_SAMPLE_DELAYS_OFFS * cs)); + dfs_reg_write(REG_READ_DATA_READY_DELAYS_ADDR, reg); + } + } + + /* Enable ODT on DLL-on mode */ + dfs_reg_write(REG_SDRAM_ODT_CTRL_HIGH_ADDR, 0); + + /* [1] - DFS Block disable */ + reg = reg_read(REG_DFS_ADDR) & ~(1 << REG_DFS_BLOCK_OFFS); + dfs_reg_write(REG_DFS_ADDR, reg); /* 0x1528 - DFS register */ + + /* Change DDR frequency to 100MHz procedure: */ + /* 0x1600 - PHY lock mask register */ + reg = reg_read(REG_ODPG_CNTRL_ADDR); + reg &= ~(1 << REG_ODPG_CNTRL_OFFS); /* [21] = 0 */ + dfs_reg_write(REG_ODPG_CNTRL_ADDR, reg); + + /* Change DDR frequency to 100MHz procedure: */ + /* 0x1670 - PHY lock mask register */ + reg = reg_read(REG_PHY_LOCK_MASK_ADDR); + reg |= ~REG_PHY_LOCK_MASK_MASK; /* [11:0] = FFF */ + dfs_reg_write(REG_PHY_LOCK_MASK_ADDR, reg); + + reg = reg_read(REG_METAL_MASK_ADDR) | (1 << 0); /* [0] - disable */ + /* 0x14B0 - Dunit MMask Register */ + dfs_reg_write(REG_METAL_MASK_ADDR, reg); + + DEBUG_DFS_C("DDR3 - DFS - Low To High - Ended successfuly - new Frequency - ", + freq, 1); + return MV_OK; +#endif +} diff --git a/drivers/ddr/mvebu/ddr3_dqs.c b/drivers/ddr/mvebu/ddr3_dqs.c new file mode 100644 index 0000000..71a986d --- /dev/null +++ b/drivers/ddr/mvebu/ddr3_dqs.c @@ -0,0 +1,1374 @@ +/* + * Copyright (C) Marvell International Ltd. and its affiliates + * + * SPDX-License-Identifier: GPL-2.0 + */ + +#include <common.h> +#include <i2c.h> +#include <spl.h> +#include <asm/io.h> +#include <asm/arch/cpu.h> +#include <asm/arch/soc.h> + +#include "ddr3_hw_training.h" + +/* + * Debug + */ +#define DEBUG_DQS_C(s, d, l) \ + DEBUG_DQS_S(s); DEBUG_DQS_D(d, l); DEBUG_DQS_S("\n") +#define DEBUG_DQS_FULL_C(s, d, l) \ + DEBUG_DQS_FULL_S(s); DEBUG_DQS_FULL_D(d, l); DEBUG_DQS_FULL_S("\n") +#define DEBUG_DQS_RESULTS_C(s, d, l) \ + DEBUG_DQS_RESULTS_S(s); DEBUG_DQS_RESULTS_D(d, l); DEBUG_DQS_RESULTS_S("\n") +#define DEBUG_PER_DQ_C(s, d, l) \ + puts(s); printf("%x", d); puts("\n") + +#define DEBUG_DQS_RESULTS_S(s) \ + debug_cond(ddr3_get_log_level() >= MV_LOG_LEVEL_2, "%s", s) +#define DEBUG_DQS_RESULTS_D(d, l) \ + debug_cond(ddr3_get_log_level() >= MV_LOG_LEVEL_2, "%x", d) + +#define DEBUG_PER_DQ_S(s) \ + debug_cond(ddr3_get_log_level() >= MV_LOG_LEVEL_3, "%s", s) +#define DEBUG_PER_DQ_D(d, l) \ + debug_cond(ddr3_get_log_level() >= MV_LOG_LEVEL_3, "%x", d) +#define DEBUG_PER_DQ_DD(d, l) \ + debug_cond(ddr3_get_log_level() >= MV_LOG_LEVEL_3, "%d", d) + +#ifdef MV_DEBUG_DQS +#define DEBUG_DQS_S(s) puts(s) +#define DEBUG_DQS_D(d, l) printf("%x", d) +#else +#define DEBUG_DQS_S(s) +#define DEBUG_DQS_D(d, l) +#endif + +#ifdef MV_DEBUG_DQS_FULL +#define DEBUG_DQS_FULL_S(s) puts(s) +#define DEBUG_DQS_FULL_D(d, l) printf("%x", d) +#else +#define DEBUG_DQS_FULL_S(s) +#define DEBUG_DQS_FULL_D(d, l) +#endif + +/* State machine for centralization - find low & high limit */ +enum { + PUP_ADLL_LIMITS_STATE_FAIL, + PUP_ADLL_LIMITS_STATE_PASS, + PUP_ADLL_LIMITS_STATE_FAIL_AFTER_PASS, +}; + +/* Hold centralization low results */ +static int centralization_low_limit[MAX_PUP_NUM] = { 0 }; +/* Hold centralization high results */ +static int centralization_high_limit[MAX_PUP_NUM] = { 0 }; + +int ddr3_find_adll_limits(MV_DRAM_INFO *dram_info, u32 cs, u32 ecc, int is_tx); +int ddr3_check_window_limits(u32 pup, int high_limit, int low_limit, int is_tx, + int *size_valid); +static int ddr3_center_calc(MV_DRAM_INFO *dram_info, u32 cs, u32 ecc, + int is_tx); +int ddr3_special_pattern_i_search(MV_DRAM_INFO *dram_info, u32 cs, u32 ecc, + int is_tx, u32 special_pattern_pup); +int ddr3_special_pattern_ii_search(MV_DRAM_INFO *dram_info, u32 cs, u32 ecc, + int is_tx, u32 special_pattern_pup); +int ddr3_set_dqs_centralization_results(MV_DRAM_INFO *dram_info, u32 cs, u32 ecc, + int is_tx); + +#ifdef MV88F78X60 +extern u32 killer_pattern_32b[DQ_NUM][LEN_SPECIAL_PATTERN]; +extern u32 killer_pattern_64b[DQ_NUM][LEN_SPECIAL_PATTERN]; +extern int per_bit_data[MAX_PUP_NUM][DQ_NUM]; +#else +extern u32 killer_pattern[DQ_NUM][LEN_16BIT_KILLER_PATTERN]; +extern u32 killer_pattern_32b[DQ_NUM][LEN_SPECIAL_PATTERN]; +#if defined(MV88F672X) +extern int per_bit_data[MAX_PUP_NUM][DQ_NUM]; +#endif +#endif +extern u32 special_pattern[DQ_NUM][LEN_SPECIAL_PATTERN]; + +static u32 *ddr3_dqs_choose_pattern(MV_DRAM_INFO *dram_info, u32 victim_dq) +{ + u32 *pattern_ptr; + + /* Choose pattern */ + switch (dram_info->ddr_width) { +#if defined(MV88F672X) + case 16: + pattern_ptr = (u32 *)&killer_pattern[victim_dq]; + break; +#endif + case 32: + pattern_ptr = (u32 *)&killer_pattern_32b[victim_dq]; + break; +#if defined(MV88F78X60) + case 64: + pattern_ptr = (u32 *)&killer_pattern_64b[victim_dq]; + break; +#endif + default: +#if defined(MV88F78X60) + pattern_ptr = (u32 *)&killer_pattern_32b[victim_dq]; +#else + pattern_ptr = (u32 *)&killer_pattern[victim_dq]; +#endif + break; + } + + return pattern_ptr; +} + +/* + * Name: ddr3_dqs_centralization_rx + * Desc: Execute the DQS centralization RX phase. + * Args: dram_info + * Notes: + * Returns: MV_OK if success, other error code if fail. + */ +int ddr3_dqs_centralization_rx(MV_DRAM_INFO *dram_info) +{ + u32 cs, ecc, reg; + int status; + + DEBUG_DQS_S("DDR3 - DQS Centralization RX - Starting procedure\n"); + + /* Enable SW override */ + reg = reg_read(REG_DRAM_TRAINING_2_ADDR) | + (1 << REG_DRAM_TRAINING_2_SW_OVRD_OFFS); + + /* [0] = 1 - Enable SW override */ + /* 0x15B8 - Training SW 2 Register */ + reg_write(REG_DRAM_TRAINING_2_ADDR, reg); + DEBUG_DQS_S("DDR3 - DQS Centralization RX - SW Override Enabled\n"); + + reg = (1 << REG_DRAM_TRAINING_AUTO_OFFS); + reg_write(REG_DRAM_TRAINING_ADDR, reg); /* 0x15B0 - Training Register */ + + /* Loop for each CS */ + for (cs = 0; cs < MAX_CS; cs++) { + if (dram_info->cs_ena & (1 << cs)) { + DEBUG_DQS_FULL_C("DDR3 - DQS Centralization RX - CS - ", + (u32) cs, 1); + + for (ecc = 0; ecc < (dram_info->ecc_ena + 1); ecc++) { + + /* ECC Support - Switch ECC Mux on ecc=1 */ + reg = reg_read(REG_DRAM_TRAINING_2_ADDR) & + ~(1 << REG_DRAM_TRAINING_2_ECC_MUX_OFFS); + reg |= (dram_info->ecc_ena * + ecc << REG_DRAM_TRAINING_2_ECC_MUX_OFFS); + reg_write(REG_DRAM_TRAINING_2_ADDR, reg); + + if (ecc) + DEBUG_DQS_FULL_S("DDR3 - DQS Centralization RX - ECC Mux Enabled\n"); + else + DEBUG_DQS_FULL_S("DDR3 - DQS Centralization RX - ECC Mux Disabled\n"); + + DEBUG_DQS_FULL_S("DDR3 - DQS Centralization RX - Find all limits\n"); + + status = ddr3_find_adll_limits(dram_info, cs, + ecc, 0); + if (MV_OK != status) + return status; + + DEBUG_DQS_FULL_S("DDR3 - DQS Centralization RX - Start calculating center\n"); + + status = ddr3_center_calc(dram_info, cs, ecc, + 0); + if (MV_OK != status) + return status; + } + } + } + + /* ECC Support - Disable ECC MUX */ + reg = reg_read(REG_DRAM_TRAINING_2_ADDR) & + ~(1 << REG_DRAM_TRAINING_2_ECC_MUX_OFFS); + reg_write(REG_DRAM_TRAINING_2_ADDR, reg); + + /* Disable SW override - Must be in a different stage */ + /* [0]=0 - Enable SW override */ + reg = reg_read(REG_DRAM_TRAINING_2_ADDR); + reg &= ~(1 << REG_DRAM_TRAINING_2_SW_OVRD_OFFS); + /* 0x15B8 - Training SW 2 Register */ + reg_write(REG_DRAM_TRAINING_2_ADDR, reg); + + reg = reg_read(REG_DRAM_TRAINING_1_ADDR) | + (1 << REG_DRAM_TRAINING_1_TRNBPOINT_OFFS); + reg_write(REG_DRAM_TRAINING_1_ADDR, reg); + + return MV_OK; +} + +/* + * Name: ddr3_dqs_centralization_tx + * Desc: Execute the DQS centralization TX phase. + * Args: dram_info + * Notes: + * Returns: MV_OK if success, other error code if fail. + */ +int ddr3_dqs_centralization_tx(MV_DRAM_INFO *dram_info) +{ + u32 cs, ecc, reg; + int status; + + DEBUG_DQS_S("DDR3 - DQS Centralization TX - Starting procedure\n"); + + /* Enable SW override */ + reg = reg_read(REG_DRAM_TRAINING_2_ADDR) | + (1 << REG_DRAM_TRAINING_2_SW_OVRD_OFFS); + + /* [0] = 1 - Enable SW override */ + /* 0x15B8 - Training SW 2 Register */ + reg_write(REG_DRAM_TRAINING_2_ADDR, reg); + DEBUG_DQS_S("DDR3 - DQS Centralization TX - SW Override Enabled\n"); + + reg = (1 << REG_DRAM_TRAINING_AUTO_OFFS); + reg_write(REG_DRAM_TRAINING_ADDR, reg); /* 0x15B0 - Training Register */ + + /* Loop for each CS */ + for (cs = 0; cs < MAX_CS; cs++) { + if (dram_info->cs_ena & (1 << cs)) { + DEBUG_DQS_FULL_C("DDR3 - DQS Centralization TX - CS - ", + (u32) cs, 1); + for (ecc = 0; ecc < (dram_info->ecc_ena + 1); ecc++) { + /* ECC Support - Switch ECC Mux on ecc=1 */ + reg = reg_read(REG_DRAM_TRAINING_2_ADDR) & + ~(1 << REG_DRAM_TRAINING_2_ECC_MUX_OFFS); + reg |= (dram_info->ecc_ena * + ecc << REG_DRAM_TRAINING_2_ECC_MUX_OFFS); + reg_write(REG_DRAM_TRAINING_2_ADDR, reg); + + if (ecc) + DEBUG_DQS_FULL_S("DDR3 - DQS Centralization TX - ECC Mux Enabled\n"); + else + DEBUG_DQS_FULL_S("DDR3 - DQS Centralization TX - ECC Mux Disabled\n"); + + DEBUG_DQS_FULL_S("DDR3 - DQS Centralization TX - Find all limits\n"); + + status = ddr3_find_adll_limits(dram_info, cs, + ecc, 1); + if (MV_OK != status) + return status; + + DEBUG_DQS_FULL_S("DDR3 - DQS Centralization TX - Start calculating center\n"); + + status = ddr3_center_calc(dram_info, cs, ecc, + 1); + if (MV_OK != status) + return status; + } + } + } + + /* ECC Support - Disable ECC MUX */ + reg = reg_read(REG_DRAM_TRAINING_2_ADDR) & + ~(1 << REG_DRAM_TRAINING_2_ECC_MUX_OFFS); + reg_write(REG_DRAM_TRAINING_2_ADDR, reg); + + /* Disable SW override - Must be in a different stage */ + /* [0]=0 - Enable SW override */ + reg = reg_read(REG_DRAM_TRAINING_2_ADDR); + reg &= ~(1 << REG_DRAM_TRAINING_2_SW_OVRD_OFFS); + /* 0x15B8 - Training SW 2 Register */ + reg_write(REG_DRAM_TRAINING_2_ADDR, reg); + + reg = reg_read(REG_DRAM_TRAINING_1_ADDR) | + (1 << REG_DRAM_TRAINING_1_TRNBPOINT_OFFS); + reg_write(REG_DRAM_TRAINING_1_ADDR, reg); + + return MV_OK; +} + +/* + * Name: ddr3_find_adll_limits + * Desc: Execute the Find ADLL limits phase. + * Args: dram_info + * cs + * ecc_ena + * is_tx Indicate whether Rx or Tx + * Notes: + * Returns: MV_OK if success, other error code if fail. + */ +int ddr3_find_adll_limits(MV_DRAM_INFO *dram_info, u32 cs, u32 ecc, int is_tx) +{ + u32 victim_dq, pup, tmp; + u32 adll_addr; + u32 max_pup; /* maximal pup index */ + u32 pup_mask = 0; + u32 unlock_pup; /* bit array of un locked pups */ + u32 new_unlock_pup; /* bit array of compare failed pups */ + u32 curr_adll; + u32 adll_start_val; /* adll start loop value - for rx or tx limit */ + u32 high_limit; /* holds found High Limit */ + u32 low_limit; /* holds found Low Limit */ + int win_valid; + int update_win; + u32 sdram_offset; + u32 uj, cs_count, cs_tmp, ii; + u32 *pattern_ptr; + u32 dq; + u32 adll_end_val; /* adll end of loop val - for rx or tx limit */ + u8 analog_pbs[DQ_NUM][MAX_PUP_NUM][DQ_NUM][2]; + u8 analog_pbs_sum[MAX_PUP_NUM][DQ_NUM][2]; + int pup_adll_limit_state[MAX_PUP_NUM]; /* hold state of each pup */ + + adll_addr = ((is_tx == 1) ? PUP_DQS_WR : PUP_DQS_RD); + adll_end_val = ((is_tx == 1) ? ADLL_MIN : ADLL_MAX); + adll_start_val = ((is_tx == 1) ? ADLL_MAX : ADLL_MIN); + max_pup = (ecc + (1 - ecc) * dram_info->num_of_std_pups); + + DEBUG_DQS_FULL_S("DDR3 - DQS Find Limits - Starting Find ADLL Limits\n"); + + /* init the array */ + for (pup = 0; pup < max_pup; pup++) { + centralization_low_limit[pup] = ADLL_MIN; + centralization_high_limit[pup] = ADLL_MAX; + } + + /* Killer Pattern */ + cs_count = 0; + for (cs_tmp = 0; cs_tmp < cs; cs_tmp++) { + if (dram_info->cs_ena & (1 << cs_tmp)) + cs_count++; + } + sdram_offset = cs_count * (SDRAM_CS_SIZE + 1); + sdram_offset += ((is_tx == 1) ? + SDRAM_DQS_TX_OFFS : SDRAM_DQS_RX_OFFS); + + /* Prepare pup masks */ + for (pup = 0; pup < max_pup; pup++) + pup_mask |= (1 << pup); + + for (pup = 0; pup < max_pup; pup++) { + for (dq = 0; dq < DQ_NUM; dq++) { + analog_pbs_sum[pup][dq][0] = adll_start_val; + analog_pbs_sum[pup][dq][1] = adll_end_val; + } + } + + /* Loop - use different pattern for each victim_dq */ + for (victim_dq = 0; victim_dq < DQ_NUM; victim_dq++) { + DEBUG_DQS_FULL_C("DDR3 - DQS Find Limits - Victim DQ - ", + (u32)victim_dq, 1); + /* + * The pups 3 bit arrays represent state machine. with + * 3 stages for each pup. + * 1. fail and didn't get pass in earlier compares. + * 2. pass compare + * 3. fail after pass - end state. + * The window limits are the adll values where the adll + * was in the pass stage. + */ + + /* Set all states to Fail (1st state) */ + for (pup = 0; pup < max_pup; pup++) + pup_adll_limit_state[pup] = PUP_ADLL_LIMITS_STATE_FAIL; + + /* Set current valid pups */ + unlock_pup = pup_mask; + + /* Set ADLL to start value */ + curr_adll = adll_start_val; + +#if defined(MV88F78X60) + for (pup = 0; pup < max_pup; pup++) { + for (dq = 0; dq < DQ_NUM; dq++) { + analog_pbs[victim_dq][pup][dq][0] = + adll_start_val; + analog_pbs[victim_dq][pup][dq][1] = + adll_end_val; + per_bit_data[pup][dq] = 0; + } + } +#endif + + for (uj = 0; uj < ADLL_MAX; uj++) { + DEBUG_DQS_FULL_C("DDR3 - DQS Find Limits - Setting ADLL to ", + curr_adll, 2); + for (pup = 0; pup < max_pup; pup++) { + if (IS_PUP_ACTIVE(unlock_pup, pup) == 1) { + tmp = ((is_tx == 1) ? curr_adll + + dram_info->wl_val[cs] + [pup * (1 - ecc) + ecc * ECC_PUP] + [D] : curr_adll); + ddr3_write_pup_reg(adll_addr, cs, pup + + (ecc * ECC_PUP), 0, tmp); + } + } + + /* Choose pattern */ + pattern_ptr = ddr3_dqs_choose_pattern(dram_info, + victim_dq); + + /* '1' - means pup failed, '0' - means pup pass */ + new_unlock_pup = 0; + + /* Read and compare results for Victim_DQ# */ + for (ii = 0; ii < 3; ii++) { + u32 tmp = 0; + if (MV_OK != ddr3_sdram_dqs_compare(dram_info, + unlock_pup, &tmp, + pattern_ptr, + LEN_KILLER_PATTERN, + sdram_offset + + LEN_KILLER_PATTERN * + 4 * victim_dq, + is_tx, 0, NULL, + 0)) + return MV_DDR3_TRAINING_ERR_DRAM_COMPARE; + + new_unlock_pup |= tmp; + } + + pup = 0; + DEBUG_DQS_FULL_C("DDR3 - DQS Find Limits - UnlockPup: ", + unlock_pup, 2); + DEBUG_DQS_FULL_C("DDR3 - DQS Find Limits - NewUnlockPup: ", + new_unlock_pup, 2); + + /* Update pup state */ + for (pup = 0; pup < max_pup; pup++) { + if (IS_PUP_ACTIVE(unlock_pup, pup) == 0) { + DEBUG_DQS_FULL_C("DDR3 - DQS Find Limits - Skipping pup ", + pup, 1); + continue; + } + + /* + * Still didn't find the window limit of the pup + */ + if (IS_PUP_ACTIVE(new_unlock_pup, pup) == 1) { + /* Current compare result == fail */ + if (pup_adll_limit_state[pup] == + PUP_ADLL_LIMITS_STATE_PASS) { + /* + * If now it failed but passed + * earlier + */ + DEBUG_DQS_S("DDR3 - DQS Find Limits - PASS to FAIL: CS - "); + DEBUG_DQS_D(cs, 1); + DEBUG_DQS_S(", DQ - "); + DEBUG_DQS_D(victim_dq, 1); + DEBUG_DQS_S(", Pup - "); + DEBUG_DQS_D(pup, 1); + DEBUG_DQS_S(", ADLL - "); + DEBUG_DQS_D(curr_adll, 2); + DEBUG_DQS_S("\n"); + +#if defined(MV88F78X60) + for (dq = 0; dq < DQ_NUM; dq++) { + if ((analog_pbs[victim_dq][pup][dq][0] != adll_start_val) + && (analog_pbs[victim_dq][pup] + [dq][1] == adll_end_val)) + analog_pbs + [victim_dq] + [pup][dq] + [1] = + curr_adll; + } +#endif + win_valid = 1; + update_win = 0; + + /* Keep min / max limit value */ + if (is_tx == 0) { + /* RX - found upper limit */ + if (centralization_high_limit[pup] > + (curr_adll - 1)) { + high_limit = + curr_adll - 1; + low_limit = + centralization_low_limit[pup]; + update_win = 1; + } + } else { + /* TX - found lower limit */ + if (centralization_low_limit[pup] < (curr_adll + 1)) { + high_limit = + centralization_high_limit + [pup]; + low_limit = + curr_adll + 1; + update_win = + 1; + } + } + + if (update_win == 1) { + /* + * Before updating + * window limits we need + * to check that the + * limits are valid + */ + if (MV_OK != + ddr3_check_window_limits + (pup, high_limit, + low_limit, is_tx, + &win_valid)) + return MV_DDR3_TRAINING_ERR_WIN_LIMITS; + + if (win_valid == 1) { + /* + * Window limits + * should be + * updated + */ + centralization_low_limit + [pup] = + low_limit; + centralization_high_limit + [pup] = + high_limit; + } + } + + if (win_valid == 1) { + /* Found end of window - lock the pup */ + pup_adll_limit_state[pup] = + PUP_ADLL_LIMITS_STATE_FAIL_AFTER_PASS; + unlock_pup &= ~(1 << pup); + } else { + /* Probably false pass - reset status */ + pup_adll_limit_state[pup] = + PUP_ADLL_LIMITS_STATE_FAIL; + +#if defined(MV88F78X60) + /* Clear logging array of win size (per Dq) */ + for (dq = 0; + dq < DQ_NUM; + dq++) { + analog_pbs + [victim_dq] + [pup][dq] + [0] = + adll_start_val; + analog_pbs + [victim_dq] + [pup][dq] + [1] = + adll_end_val; + per_bit_data + [pup][dq] + = 0; + } +#endif + } + } + } else { + /* Current compare result == pass */ + if (pup_adll_limit_state[pup] == + PUP_ADLL_LIMITS_STATE_FAIL) { + /* If now it passed but failed earlier */ + DEBUG_DQS_S("DDR3 - DQS Find Limits - FAIL to PASS: CS - "); + DEBUG_DQS_D(cs, 1); + DEBUG_DQS_S(", DQ - "); + DEBUG_DQS_D(victim_dq, 1); + DEBUG_DQS_S(", Pup - "); + DEBUG_DQS_D(pup, 1); + DEBUG_DQS_S(", ADLL - "); + DEBUG_DQS_D(curr_adll, 2); + DEBUG_DQS_S("\n"); + +#if defined(MV88F78X60) + for (dq = 0; dq < DQ_NUM; + dq++) { + if (analog_pbs[victim_dq][pup][dq][0] == adll_start_val) + analog_pbs + [victim_dq] + [pup][dq] + [0] = + curr_adll; + } +#endif + /* Found start of window */ + pup_adll_limit_state[pup] = + PUP_ADLL_LIMITS_STATE_PASS; + + /* Keep min / max limit value */ + if (is_tx == 0) { + /* RX - found low limit */ + if (centralization_low_limit[pup] <= curr_adll) + centralization_low_limit + [pup] = + curr_adll; + } else { + /* TX - found high limit */ + if (centralization_high_limit[pup] >= curr_adll) + centralization_high_limit + [pup] = + curr_adll; + } + } + } + } + + if (unlock_pup == 0) { + /* Found limit to all pups */ + DEBUG_DQS_FULL_S("DDR3 - DQS Find Limits - found PUP limit\n"); + break; + } + + /* + * Increment / decrement (Move to right / left + * one phase - ADLL) dqs RX / TX delay (for all un + * lock pups + */ + if (is_tx == 0) + curr_adll++; + else + curr_adll--; + } + + if (unlock_pup != 0) { + /* + * Found pups that didn't reach to the end of the + * state machine + */ + DEBUG_DQS_C("DDR3 - DQS Find Limits - Pups that didn't reached end of the state machine: ", + unlock_pup, 1); + + for (pup = 0; pup < max_pup; pup++) { + if (IS_PUP_ACTIVE(unlock_pup, pup) == 1) { + if (pup_adll_limit_state[pup] == + PUP_ADLL_LIMITS_STATE_FAIL) { + /* ERROR - found fail for all window size */ + DEBUG_DQS_S("DDR3 - DQS Find Limits - Got FAIL for the complete range on pup - "); + DEBUG_DQS_D(pup, 1); + DEBUG_DQS_C(" victim DQ ", + victim_dq, 1); + + /* For debug - set min limit to illegal limit */ + centralization_low_limit[pup] + = ADLL_ERROR; + /* + * In case the pup is in mode + * PASS - the limit is the min + * / max adll, no need to + * update because of the results + * array default value + */ + return MV_DDR3_TRAINING_ERR_PUP_RANGE; + } + } + } + } + } + + DEBUG_DQS_S("DDR3 - DQS Find Limits - DQ values per victim results:\n"); + for (victim_dq = 0; victim_dq < DQ_NUM; victim_dq++) { + for (pup = 0; pup < max_pup; pup++) { + DEBUG_DQS_S("Victim DQ-"); + DEBUG_DQS_D(victim_dq, 1); + DEBUG_DQS_S(", PUP-"); + DEBUG_DQS_D(pup, 1); + for (dq = 0; dq < DQ_NUM; dq++) { + DEBUG_DQS_S(", DQ-"); + DEBUG_DQS_D(dq, 1); + DEBUG_DQS_S(",S-"); + DEBUG_DQS_D(analog_pbs[victim_dq][pup][dq] + [0], 2); + DEBUG_DQS_S(",E-"); + DEBUG_DQS_D(analog_pbs[victim_dq][pup][dq] + [1], 2); + + if (is_tx == 0) { + if (analog_pbs[victim_dq][pup][dq][0] + > analog_pbs_sum[pup][dq][0]) + analog_pbs_sum[pup][dq][0] = + analog_pbs[victim_dq][pup] + [dq][0]; + if (analog_pbs[victim_dq][pup][dq][1] + < analog_pbs_sum[pup][dq][1]) + analog_pbs_sum[pup][dq][1] = + analog_pbs[victim_dq][pup] + [dq][1]; + } else { + if (analog_pbs[victim_dq][pup][dq][0] + < analog_pbs_sum[pup][dq][0]) + analog_pbs_sum[pup][dq][0] = + analog_pbs[victim_dq][pup] + [dq][0]; + if (analog_pbs[victim_dq][pup][dq][1] + > analog_pbs_sum[pup][dq][1]) + analog_pbs_sum[pup][dq][1] = + analog_pbs[victim_dq][pup] + [dq][1]; + } + } + DEBUG_DQS_S("\n"); + } + } + + if (ddr3_get_log_level() >= MV_LOG_LEVEL_3) { + u32 dq; + + DEBUG_PER_DQ_S("\n########## LOG LEVEL 3(Windows margins per-DQ) ##########\n"); + if (is_tx) { + DEBUG_PER_DQ_C("DDR3 - TX CS: ", cs, 1); + } else { + DEBUG_PER_DQ_C("DDR3 - RX CS: ", cs, 1); + } + + if (ecc == 0) { + DEBUG_PER_DQ_S("\n DATA RESULTS:\n"); + } else { + DEBUG_PER_DQ_S("\n ECC RESULTS:\n"); + } + + /* Since all dq has the same value we take 0 as representive */ + dq = 0; + for (pup = 0; pup < max_pup; pup++) { + if (ecc == 0) { + DEBUG_PER_DQ_S("\nBYTE:"); + DEBUG_PER_DQ_D(pup, 1); + DEBUG_PER_DQ_S("\n"); + } else { + DEBUG_PER_DQ_S("\nECC BYTE:\n"); + } + DEBUG_PER_DQ_S(" DQ's LOW HIGH WIN-SIZE\n"); + DEBUG_PER_DQ_S("============================================\n"); + for (victim_dq = 0; victim_dq < DQ_NUM; victim_dq++) { + if (ecc == 0) { + DEBUG_PER_DQ_S("DQ["); + DEBUG_PER_DQ_DD((victim_dq + + DQ_NUM * pup), 2); + DEBUG_PER_DQ_S("]"); + } else { + DEBUG_PER_DQ_S("CB["); + DEBUG_PER_DQ_DD(victim_dq, 2); + DEBUG_PER_DQ_S("]"); + } + if (is_tx) { + DEBUG_PER_DQ_S(" 0x"); + DEBUG_PER_DQ_D(analog_pbs[victim_dq][pup][dq][1], 2); /* low value */ + DEBUG_PER_DQ_S(" 0x"); + DEBUG_PER_DQ_D(analog_pbs[victim_dq][pup][dq][0], 2); /* high value */ + DEBUG_PER_DQ_S(" 0x"); + DEBUG_PER_DQ_D(analog_pbs[victim_dq][pup][dq][0] - analog_pbs[victim_dq][pup][dq][1], 2); /* win-size */ + } else { + DEBUG_PER_DQ_S(" 0x"); + DEBUG_PER_DQ_D(analog_pbs[victim_dq][pup][dq][0], 2); /* low value */ + DEBUG_PER_DQ_S(" 0x"); + DEBUG_PER_DQ_D((analog_pbs[victim_dq][pup][dq][1] - 1), 2); /* high value */ + DEBUG_PER_DQ_S(" 0x"); + DEBUG_PER_DQ_D(analog_pbs[victim_dq][pup][dq][1] - analog_pbs[victim_dq][pup][dq][0], 2); /* win-size */ + } + DEBUG_PER_DQ_S("\n"); + } + } + DEBUG_PER_DQ_S("\n"); + } + + if (is_tx) { + DEBUG_DQS_S("DDR3 - DQS TX - Find Limits - DQ values Summary:\n"); + } else { + DEBUG_DQS_S("DDR3 - DQS RX - Find Limits - DQ values Summary:\n"); + } + + for (pup = 0; pup < max_pup; pup++) { + DEBUG_DQS_S("PUP-"); + DEBUG_DQS_D(pup, 1); + for (dq = 0; dq < DQ_NUM; dq++) { + DEBUG_DQS_S(", DQ-"); + DEBUG_DQS_D(dq, 1); + DEBUG_DQS_S(",S-"); + DEBUG_DQS_D(analog_pbs_sum[pup][dq][0], 2); + DEBUG_DQS_S(",E-"); + DEBUG_DQS_D(analog_pbs_sum[pup][dq][1], 2); + } + DEBUG_DQS_S("\n"); + } + + if (is_tx) { + DEBUG_DQS_S("DDR3 - DQS TX - Find Limits - DQ values Summary:\n"); + } else { + DEBUG_DQS_S("DDR3 - DQS RX - Find Limits - DQ values Summary:\n"); + } + + for (pup = 0; pup < max_pup; pup++) { + if (max_pup == 1) { + /* For ECC PUP */ + DEBUG_DQS_S("DDR3 - DQS8"); + } else { + DEBUG_DQS_S("DDR3 - DQS"); + DEBUG_DQS_D(pup, 1); + } + + for (dq = 0; dq < DQ_NUM; dq++) { + DEBUG_DQS_S(", DQ-"); + DEBUG_DQS_D(dq, 1); + DEBUG_DQS_S("::S-"); + DEBUG_DQS_D(analog_pbs_sum[pup][dq][0], 2); + DEBUG_DQS_S(",E-"); + DEBUG_DQS_D(analog_pbs_sum[pup][dq][1], 2); + } + DEBUG_DQS_S("\n"); + } + + DEBUG_DQS_S("DDR3 - DQS Find Limits - Ended\n"); + + return MV_OK; +} + +/* + * Name: ddr3_check_window_limits + * Desc: Check window High & Low limits. + * Args: pup pup index + * high_limit window high limit + * low_limit window low limit + * is_tx Indicate whether Rx or Tx + * size_valid Indicate whether window size is valid + * Notes: + * Returns: MV_OK if success, other error code if fail. + */ +int ddr3_check_window_limits(u32 pup, int high_limit, int low_limit, int is_tx, + int *size_valid) +{ + DEBUG_DQS_FULL_S("DDR3 - DQS Check Win Limits - Starting\n"); + + if (low_limit > high_limit) { + DEBUG_DQS_S("DDR3 - DQS Check Win Limits - Pup "); + DEBUG_DQS_D(pup, 1); + DEBUG_DQS_S(" Low Limit grater than High Limit\n"); + *size_valid = 0; + return MV_OK; + } + + /* + * Check that window size is valid, if not it was probably false pass + * before + */ + if ((high_limit - low_limit) < MIN_WIN_SIZE) { + /* + * Since window size is too small probably there was false + * pass + */ + *size_valid = 0; + + DEBUG_DQS_S("DDR3 - DQS Check Win Limits - Pup "); + DEBUG_DQS_D(pup, 1); + DEBUG_DQS_S(" Window size is smaller than MIN_WIN_SIZE\n"); + + } else if ((high_limit - low_limit) > ADLL_MAX) { + *size_valid = 0; + + DEBUG_DQS_S("DDR3 - DQS Check Win Limits - Pup "); + DEBUG_DQS_D(pup, 1); + DEBUG_DQS_S + (" Window size is bigger than max ADLL taps (31) Exiting.\n"); + + return MV_FAIL; + + } else { + *size_valid = 1; + + DEBUG_DQS_FULL_S("DDR3 - DQS Check Win Limits - Pup "); + DEBUG_DQS_FULL_D(pup, 1); + DEBUG_DQS_FULL_C(" window size is ", (high_limit - low_limit), + 2); + } + + return MV_OK; +} + +/* + * Name: ddr3_center_calc + * Desc: Execute the calculate the center of windows phase. + * Args: pDram Info + * is_tx Indicate whether Rx or Tx + * Notes: + * Returns: MV_OK if success, other error code if fail. + */ +static int ddr3_center_calc(MV_DRAM_INFO *dram_info, u32 cs, u32 ecc, + int is_tx) +{ + /* bit array of pups that need specail search */ + u32 special_pattern_i_pup = 0; + u32 special_pattern_ii_pup = 0; + u32 pup; + u32 max_pup; + + max_pup = (ecc + (1 - ecc) * dram_info->num_of_std_pups); + + for (pup = 0; pup < max_pup; pup++) { + if (is_tx == 0) { + /* Check special pattern I */ + /* + * Special pattern Low limit search - relevant only + * for Rx, win size < threshold and low limit = 0 + */ + if (((centralization_high_limit[pup] - + centralization_low_limit[pup]) < VALID_WIN_THRS) + && (centralization_low_limit[pup] == MIN_DELAY)) + special_pattern_i_pup |= (1 << pup); + + /* Check special pattern II */ + /* + * Special pattern High limit search - relevant only + * for Rx, win size < threshold and high limit = 31 + */ + if (((centralization_high_limit[pup] - + centralization_low_limit[pup]) < VALID_WIN_THRS) + && (centralization_high_limit[pup] == MAX_DELAY)) + special_pattern_ii_pup |= (1 << pup); + } + } + + /* Run special pattern Low limit search - for relevant pup */ + if (special_pattern_i_pup != 0) { + DEBUG_DQS_S("DDR3 - DQS Center Calc - Entering special pattern I for Low limit search\n"); + if (MV_OK != + ddr3_special_pattern_i_search(dram_info, cs, ecc, is_tx, + special_pattern_i_pup)) + return MV_DDR3_TRAINING_ERR_DQS_LOW_LIMIT_SEARCH; + } + + /* Run special pattern High limit search - for relevant pup */ + if (special_pattern_ii_pup != 0) { + DEBUG_DQS_S("DDR3 - DQS Center Calc - Entering special pattern II for High limit search\n"); + if (MV_OK != + ddr3_special_pattern_ii_search(dram_info, cs, ecc, is_tx, + special_pattern_ii_pup)) + return MV_DDR3_TRAINING_ERR_DQS_HIGH_LIMIT_SEARCH; + } + + /* Set adll to center = (General_High_limit + General_Low_limit)/2 */ + return ddr3_set_dqs_centralization_results(dram_info, cs, ecc, is_tx); +} + +/* + * Name: ddr3_special_pattern_i_search + * Desc: Execute special pattern low limit search. + * Args: + * special_pattern_pup The pups that need the special search + * Notes: + * Returns: MV_OK if success, other error code if fail. + */ +int ddr3_special_pattern_i_search(MV_DRAM_INFO *dram_info, u32 cs, u32 ecc, + int is_tx, u32 special_pattern_pup) +{ + u32 victim_dq; /* loop index - victim DQ */ + u32 adll_idx; + u32 pup; + u32 unlock_pup; /* bit array of the unlock pups */ + u32 first_fail; /* bit array - of pups that get first fail */ + u32 new_lockup_pup; /* bit array of compare failed pups */ + u32 pass_pup; /* bit array of compare pass pup */ + u32 sdram_offset; + u32 max_pup; + u32 comp_val; + u32 special_res[MAX_PUP_NUM]; /* hold tmp results */ + + DEBUG_DQS_S("DDR3 - DQS - Special Pattern I Search - Starting\n"); + + max_pup = ecc + (1 - ecc) * dram_info->num_of_std_pups; + + /* Init the temporary results to max ADLL value */ + for (pup = 0; pup < max_pup; pup++) + special_res[pup] = ADLL_MAX; + + /* Run special pattern for all DQ - use the same pattern */ + for (victim_dq = 0; victim_dq < DQ_NUM; victim_dq++) { + unlock_pup = special_pattern_pup; + first_fail = 0; + + sdram_offset = cs * SDRAM_CS_SIZE + SDRAM_DQS_RX_OFFS + + LEN_KILLER_PATTERN * 4 * victim_dq; + + for (pup = 0; pup < max_pup; pup++) { + /* Set adll value per PUP. adll = high limit per pup */ + if (IS_PUP_ACTIVE(unlock_pup, pup)) { + /* only for pups that need special search */ + ddr3_write_pup_reg(PUP_DQS_RD, cs, + pup + (ecc * ECC_PUP), 0, + centralization_high_limit + [pup]); + } + } + + adll_idx = 0; + do { + /* + * Perform read and compare simultaneously for all + * un-locked MC use the special pattern mask + */ + new_lockup_pup = 0; + + if (MV_OK != + ddr3_sdram_dqs_compare(dram_info, unlock_pup, + &new_lockup_pup, + special_pattern + [victim_dq], + LEN_SPECIAL_PATTERN, + sdram_offset, 0, + 0, NULL, 1)) + return MV_FAIL; + + DEBUG_DQS_S("DDR3 - DQS - Special I - ADLL value is: "); + DEBUG_DQS_D(adll_idx, 2); + DEBUG_DQS_S(", UnlockPup: "); + DEBUG_DQS_D(unlock_pup, 2); + DEBUG_DQS_S(", NewLockPup: "); + DEBUG_DQS_D(new_lockup_pup, 2); + DEBUG_DQS_S("\n"); + + if (unlock_pup != new_lockup_pup) + DEBUG_DQS_S("DDR3 - DQS - Special I - Some Pup passed!\n"); + + /* Search for pups with passed compare & already fail */ + pass_pup = first_fail & ~new_lockup_pup & unlock_pup; + first_fail |= new_lockup_pup; + unlock_pup &= ~pass_pup; + + /* Get pass pups */ + if (pass_pup != 0) { + for (pup = 0; pup < max_pup; pup++) { + if (IS_PUP_ACTIVE(pass_pup, pup) == + 1) { + /* If pup passed and has first fail = 1 */ + /* keep min value of ADLL max value - current adll */ + /* (centralization_high_limit[pup] + adll_idx) = current adll !!! */ + comp_val = + (ADLL_MAX - + (centralization_high_limit + [pup] + adll_idx)); + + DEBUG_DQS_C + ("DDR3 - DQS - Special I - Pup - ", + pup, 1); + DEBUG_DQS_C + (" comp_val = ", + comp_val, 2); + + if (comp_val < + special_res[pup]) { + special_res[pup] = + comp_val; + centralization_low_limit + [pup] = + (-1) * + comp_val; + + DEBUG_DQS_C + ("DDR3 - DQS - Special I - Pup - ", + pup, 1); + DEBUG_DQS_C + (" Changed Low limit to ", + centralization_low_limit + [pup], 2); + } + } + } + } + + /* + * Did all PUP found missing window? + * Check for each pup if adll (different for each pup) + * reach maximum if reach max value - lock the pup + * if not - increment (Move to right one phase - ADLL) + * dqs RX delay + */ + adll_idx++; + for (pup = 0; pup < max_pup; pup++) { + if (IS_PUP_ACTIVE(unlock_pup, pup) == 1) { + /* Check only unlocked pups */ + if ((centralization_high_limit[pup] + + adll_idx) >= ADLL_MAX) { + /* reach maximum - lock the pup */ + DEBUG_DQS_C("DDR3 - DQS - Special I - reach maximum - lock pup ", + pup, 1); + unlock_pup &= ~(1 << pup); + } else { + /* Didn't reach maximum - increment ADLL */ + ddr3_write_pup_reg(PUP_DQS_RD, + cs, + pup + + (ecc * + ECC_PUP), 0, + (centralization_high_limit + [pup] + + adll_idx)); + } + } + } + } while (unlock_pup != 0); + } + + return MV_OK; +} + +/* + * Name: ddr3_special_pattern_ii_search + * Desc: Execute special pattern high limit search. + * Args: + * special_pattern_pup The pups that need the special search + * Notes: + * Returns: MV_OK if success, other error code if fail. + */ +int ddr3_special_pattern_ii_search(MV_DRAM_INFO *dram_info, u32 cs, u32 ecc, + int is_tx, u32 special_pattern_pup) +{ + u32 victim_dq; /* loop index - victim DQ */ + u32 adll_idx; + u32 pup; + u32 unlock_pup; /* bit array of the unlock pups */ + u32 first_fail; /* bit array - of pups that get first fail */ + u32 new_lockup_pup; /* bit array of compare failed pups */ + u32 pass_pup; /* bit array of compare pass pup */ + u32 sdram_offset; + u32 max_pup; + u32 comp_val; + u32 special_res[MAX_PUP_NUM]; /* hold tmp results */ + + DEBUG_DQS_S("DDR3 - DQS - Special Pattern II Search - Starting\n"); + + max_pup = (ecc + (1 - ecc) * dram_info->num_of_std_pups); + + /* init the tmporary results to max ADLL value */ + for (pup = 0; pup < max_pup; pup++) + special_res[pup] = ADLL_MAX; + + sdram_offset = cs * SDRAM_CS_SIZE + SDRAM_DQS_RX_OFFS; + + /* run special pattern for all DQ - use the same pattern */ + for (victim_dq = 0; victim_dq < DQ_NUM; victim_dq++) { + unlock_pup = special_pattern_pup; + first_fail = 0; + + for (pup = 0; pup < max_pup; pup++) { + /* Set adll value per PUP. adll = 0 */ + if (IS_PUP_ACTIVE(unlock_pup, pup)) { + /* Only for pups that need special search */ + ddr3_write_pup_reg(PUP_DQS_RD, cs, + pup + (ecc * ECC_PUP), 0, + ADLL_MIN); + } + } + + adll_idx = 0; + do { + /* + * Perform read and compare simultaneously for all + * un-locked MC use the special pattern mask + */ + new_lockup_pup = 0; + + if (MV_OK != ddr3_sdram_dqs_compare( + dram_info, unlock_pup, &new_lockup_pup, + special_pattern[victim_dq], + LEN_SPECIAL_PATTERN, + sdram_offset, 0, 0, NULL, 0)) + return MV_FAIL; + + DEBUG_DQS_S("DDR3 - DQS - Special II - ADLL value is "); + DEBUG_DQS_D(adll_idx, 2); + DEBUG_DQS_S("unlock_pup "); + DEBUG_DQS_D(unlock_pup, 1); + DEBUG_DQS_S("new_lockup_pup "); + DEBUG_DQS_D(new_lockup_pup, 1); + DEBUG_DQS_S("\n"); + + if (unlock_pup != new_lockup_pup) { + DEBUG_DQS_S("DDR3 - DQS - Special II - Some Pup passed!\n"); + } + + /* Search for pups with passed compare & already fail */ + pass_pup = first_fail & ~new_lockup_pup & unlock_pup; + first_fail |= new_lockup_pup; + unlock_pup &= ~pass_pup; + + /* Get pass pups */ + if (pass_pup != 0) { + for (pup = 0; pup < max_pup; pup++) { + if (IS_PUP_ACTIVE(pass_pup, pup) == + 1) { + /* If pup passed and has first fail = 1 */ + /* keep min value of ADLL max value - current adll */ + /* (adll_idx) = current adll !!! */ + comp_val = adll_idx; + + DEBUG_DQS_C("DDR3 - DQS - Special II - Pup - ", + pup, 1); + DEBUG_DQS_C(" comp_val = ", + comp_val, 1); + + if (comp_val < + special_res[pup]) { + special_res[pup] = + comp_val; + centralization_high_limit + [pup] = + ADLL_MAX + + comp_val; + + DEBUG_DQS_C + ("DDR3 - DQS - Special II - Pup - ", + pup, 1); + DEBUG_DQS_C + (" Changed High limit to ", + centralization_high_limit + [pup], 2); + } + } + } + } + + /* + * Did all PUP found missing window? + * Check for each pup if adll (different for each pup) + * reach maximum if reach max value - lock the pup + * if not - increment (Move to right one phase - ADLL) + * dqs RX delay + */ + adll_idx++; + for (pup = 0; pup < max_pup; pup++) { + if (IS_PUP_ACTIVE(unlock_pup, pup) == 1) { + /* Check only unlocked pups */ + if ((adll_idx) >= ADLL_MAX) { + /* Reach maximum - lock the pup */ + DEBUG_DQS_C("DDR3 - DQS - Special II - reach maximum - lock pup ", + pup, 1); + unlock_pup &= ~(1 << pup); + } else { + /* Didn't reach maximum - increment ADLL */ + ddr3_write_pup_reg(PUP_DQS_RD, + cs, + pup + + (ecc * + ECC_PUP), 0, + (adll_idx)); + } + } + } + } while (unlock_pup != 0); + } + + return MV_OK; +} + +/* + * Name: ddr3_set_dqs_centralization_results + * Desc: Set to HW the DQS centralization phase results. + * Args: + * is_tx Indicates whether to set Tx or RX results + * Notes: + * Returns: MV_OK if success, other error code if fail. + */ +int ddr3_set_dqs_centralization_results(MV_DRAM_INFO *dram_info, u32 cs, + u32 ecc, int is_tx) +{ + u32 pup, pup_num; + int addl_val; + u32 max_pup; + + max_pup = (ecc + (1 - ecc) * dram_info->num_of_std_pups); + + DEBUG_DQS_RESULTS_S("\n############ LOG LEVEL 2(Windows margins) ############\n");; + + if (is_tx) { + DEBUG_DQS_RESULTS_C("DDR3 - DQS TX - Set Dqs Centralization Results - CS: ", + cs, 1); + } else { + DEBUG_DQS_RESULTS_C("DDR3 - DQS RX - Set Dqs Centralization Results - CS: ", + cs, 1); + } + + /* Set adll to center = (General_High_limit + General_Low_limit)/2 */ + DEBUG_DQS_RESULTS_S("\nDQS LOW HIGH WIN-SIZE Set\n"); + DEBUG_DQS_RESULTS_S("==============================================\n"); + for (pup = 0; pup < max_pup; pup++) { + addl_val = (centralization_high_limit[pup] + + centralization_low_limit[pup]) / 2; + + pup_num = pup * (1 - ecc) + ecc * ECC_PUP; + + DEBUG_DQS_RESULTS_D(pup_num, 1); + DEBUG_DQS_RESULTS_S(" 0x"); + DEBUG_DQS_RESULTS_D(centralization_low_limit[pup], 2); + DEBUG_DQS_RESULTS_S(" 0x"); + DEBUG_DQS_RESULTS_D(centralization_high_limit[pup], 2); + DEBUG_DQS_RESULTS_S(" 0x"); + DEBUG_DQS_RESULTS_D(centralization_high_limit[pup] - + centralization_low_limit[pup], 2); + DEBUG_DQS_RESULTS_S(" 0x"); + DEBUG_DQS_RESULTS_D(addl_val, 2); + DEBUG_DQS_RESULTS_S("\n"); + + if (addl_val < ADLL_MIN) { + addl_val = ADLL_MIN; + DEBUG_DQS_RESULTS_S("DDR3 - DQS - Setting ADLL value for Pup to MIN (since it was lower than 0)\n"); + } + + if (addl_val > ADLL_MAX) { + addl_val = ADLL_MAX; + DEBUG_DQS_RESULTS_S("DDR3 - DQS - Setting ADLL value for Pup to MAX (since it was higher than 31)\n"); + } + + if (is_tx) { + ddr3_write_pup_reg(PUP_DQS_WR, cs, pup_num, 0, + addl_val + + dram_info->wl_val[cs][pup_num][D]); + } else { + ddr3_write_pup_reg(PUP_DQS_RD, cs, pup_num, 0, + addl_val); + } + } + + return MV_OK; +} + +/* + * Set training patterns + */ +int ddr3_load_dqs_patterns(MV_DRAM_INFO *dram_info) +{ + u32 cs, cs_count, cs_tmp, victim_dq; + u32 sdram_addr; + u32 *pattern_ptr; + + /* Loop for each CS */ + for (cs = 0; cs < MAX_CS; cs++) { + if (dram_info->cs_ena & (1 << cs)) { + cs_count = 0; + for (cs_tmp = 0; cs_tmp < cs; cs_tmp++) { + if (dram_info->cs_ena & (1 << cs_tmp)) + cs_count++; + } + + /* Init killer pattern */ + sdram_addr = (cs_count * (SDRAM_CS_SIZE + 1) + + SDRAM_DQS_RX_OFFS); + for (victim_dq = 0; victim_dq < DQ_NUM; victim_dq++) { + pattern_ptr = ddr3_dqs_choose_pattern(dram_info, + victim_dq); + if (MV_OK != ddr3_sdram_dqs_compare( + dram_info, (u32)NULL, NULL, + pattern_ptr, LEN_KILLER_PATTERN, + sdram_addr + LEN_KILLER_PATTERN * + 4 * victim_dq, 1, 0, NULL, + 0)) + return MV_DDR3_TRAINING_ERR_DQS_PATTERN; + } + + /* Init special-killer pattern */ + sdram_addr = (cs_count * (SDRAM_CS_SIZE + 1) + + SDRAM_DQS_RX_SPECIAL_OFFS); + for (victim_dq = 0; victim_dq < DQ_NUM; victim_dq++) { + if (MV_OK != ddr3_sdram_dqs_compare( + dram_info, (u32)NULL, NULL, + special_pattern[victim_dq], + LEN_KILLER_PATTERN, sdram_addr + + LEN_KILLER_PATTERN * 4 * victim_dq, + 1, 0, NULL, 0)) + return MV_DDR3_TRAINING_ERR_DQS_PATTERN; + } + } + } + + return MV_OK; +} diff --git a/drivers/ddr/mvebu/ddr3_hw_training.c b/drivers/ddr/mvebu/ddr3_hw_training.c new file mode 100644 index 0000000..a8c5e6a --- /dev/null +++ b/drivers/ddr/mvebu/ddr3_hw_training.c @@ -0,0 +1,1115 @@ +/* + * Copyright (C) Marvell International Ltd. and its affiliates + * + * SPDX-License-Identifier: GPL-2.0 + */ + +#include <common.h> +#include <i2c.h> +#include <spl.h> +#include <asm/io.h> +#include <asm/arch/cpu.h> +#include <asm/arch/soc.h> + +#include "ddr3_init.h" +#include "ddr3_hw_training.h" +#include "xor.h" + +#ifdef MV88F78X60 +#include "ddr3_patterns_64bit.h" +#else +#include "ddr3_patterns_16bit.h" +#if defined(MV88F672X) +#include "ddr3_patterns_16bit.h" +#endif +#endif + +/* + * Debug + */ + +#define DEBUG_MAIN_C(s, d, l) \ + DEBUG_MAIN_S(s); DEBUG_MAIN_D(d, l); DEBUG_MAIN_S("\n") +#define DEBUG_MAIN_FULL_C(s, d, l) \ + DEBUG_MAIN_FULL_S(s); DEBUG_MAIN_FULL_D(d, l); DEBUG_MAIN_FULL_S("\n") + +#ifdef MV_DEBUG_MAIN +#define DEBUG_MAIN_S(s) puts(s) +#define DEBUG_MAIN_D(d, l) printf("%x", d) +#else +#define DEBUG_MAIN_S(s) +#define DEBUG_MAIN_D(d, l) +#endif + +#ifdef MV_DEBUG_MAIN_FULL +#define DEBUG_MAIN_FULL_S(s) puts(s) +#define DEBUG_MAIN_FULL_D(d, l) printf("%x", d) +#else +#define DEBUG_MAIN_FULL_S(s) +#define DEBUG_MAIN_FULL_D(d, l) +#endif + +#ifdef MV_DEBUG_SUSPEND_RESUME +#define DEBUG_SUSPEND_RESUME_S(s) puts(s) +#define DEBUG_SUSPEND_RESUME_D(d, l) printf("%x", d) +#else +#define DEBUG_SUSPEND_RESUME_S(s) +#define DEBUG_SUSPEND_RESUME_D(d, l) +#endif + +static u32 ddr3_sw_wl_rl_debug; +static u32 ddr3_run_pbs = 1; + +void ddr3_print_version(void) +{ + puts("DDR3 Training Sequence - Ver 5.7."); +} + +void ddr3_set_sw_wl_rl_debug(u32 val) +{ + ddr3_sw_wl_rl_debug = val; +} + +void ddr3_set_pbs(u32 val) +{ + ddr3_run_pbs = val; +} + +int ddr3_hw_training(u32 target_freq, u32 ddr_width, int xor_bypass, + u32 scrub_offs, u32 scrub_size, int dqs_clk_aligned, + int debug_mode, int reg_dimm_skip_wl) +{ + /* A370 has no PBS mechanism */ + __maybe_unused u32 first_loop_flag = 0; + u32 freq, reg; + MV_DRAM_INFO dram_info; + int ratio_2to1 = 0; + int tmp_ratio = 1; + int status; + + if (debug_mode) + DEBUG_MAIN_S("DDR3 Training Sequence - DEBUG - 1\n"); + + memset(&dram_info, 0, sizeof(dram_info)); + dram_info.num_cs = ddr3_get_cs_num_from_reg(); + dram_info.cs_ena = ddr3_get_cs_ena_from_reg(); + dram_info.target_frequency = target_freq; + dram_info.ddr_width = ddr_width; + dram_info.num_of_std_pups = ddr_width / PUP_SIZE; + dram_info.rl400_bug = 0; + dram_info.multi_cs_mr_support = 0; +#ifdef MV88F67XX + dram_info.rl400_bug = 1; +#endif + + /* Ignore ECC errors - if ECC is enabled */ + reg = reg_read(REG_SDRAM_CONFIG_ADDR); + if (reg & (1 << REG_SDRAM_CONFIG_ECC_OFFS)) { + dram_info.ecc_ena = 1; + reg |= (1 << REG_SDRAM_CONFIG_IERR_OFFS); + reg_write(REG_SDRAM_CONFIG_ADDR, reg); + } else { + dram_info.ecc_ena = 0; + } + + reg = reg_read(REG_SDRAM_CONFIG_ADDR); + if (reg & (1 << REG_SDRAM_CONFIG_REGDIMM_OFFS)) + dram_info.reg_dimm = 1; + else + dram_info.reg_dimm = 0; + + dram_info.num_of_total_pups = ddr_width / PUP_SIZE + dram_info.ecc_ena; + + /* Get target 2T value */ + reg = reg_read(REG_DUNIT_CTRL_LOW_ADDR); + dram_info.mode_2t = (reg >> REG_DUNIT_CTRL_LOW_2T_OFFS) & + REG_DUNIT_CTRL_LOW_2T_MASK; + + /* Get target CL value */ +#ifdef MV88F67XX + reg = reg_read(REG_DDR3_MR0_ADDR) >> 2; +#else + reg = reg_read(REG_DDR3_MR0_CS_ADDR) >> 2; +#endif + + reg = (((reg >> 1) & 0xE) | (reg & 0x1)) & 0xF; + dram_info.cl = ddr3_valid_cl_to_cl(reg); + + /* Get target CWL value */ +#ifdef MV88F67XX + reg = reg_read(REG_DDR3_MR2_ADDR) >> REG_DDR3_MR2_CWL_OFFS; +#else + reg = reg_read(REG_DDR3_MR2_CS_ADDR) >> REG_DDR3_MR2_CWL_OFFS; +#endif + + reg &= REG_DDR3_MR2_CWL_MASK; + dram_info.cwl = reg; +#if !defined(MV88F67XX) + /* A370 has no PBS mechanism */ +#if defined(MV88F78X60) + if ((dram_info.target_frequency > DDR_400) && (ddr3_run_pbs)) + first_loop_flag = 1; +#else + /* first_loop_flag = 1; skip mid freq at ALP/A375 */ + if ((dram_info.target_frequency > DDR_400) && (ddr3_run_pbs) && + (mv_ctrl_revision_get() >= UMC_A0)) + first_loop_flag = 1; + else + first_loop_flag = 0; +#endif +#endif + + freq = dram_info.target_frequency; + + /* Set ODT to always on */ + ddr3_odt_activate(1); + + /* Init XOR */ + mv_sys_xor_init(&dram_info); + + /* Get DRAM/HCLK ratio */ + if (reg_read(REG_DDR_IO_ADDR) & (1 << REG_DDR_IO_CLK_RATIO_OFFS)) + ratio_2to1 = 1; + + /* + * Xor Bypass - ECC support in AXP is currently available for 1:1 + * modes frequency modes. + * Not all frequency modes support the ddr3 training sequence + * (Only 1200/300). + * Xor Bypass allows using the Xor initializations and scrubbing + * inside the ddr3 training sequence without running the training + * itself. + */ + if (xor_bypass == 0) { + if (ddr3_run_pbs) { + DEBUG_MAIN_S("DDR3 Training Sequence - Run with PBS.\n"); + } else { + DEBUG_MAIN_S("DDR3 Training Sequence - Run without PBS.\n"); + } + + if (dram_info.target_frequency > DFS_MARGIN) { + tmp_ratio = 0; + freq = DDR_100; + + if (dram_info.reg_dimm == 1) + freq = DDR_300; + + if (MV_OK != ddr3_dfs_high_2_low(freq, &dram_info)) { + /* Set low - 100Mhz DDR Frequency by HW */ + DEBUG_MAIN_S("DDR3 Training Sequence - FAILED (Dfs High2Low)\n"); + return MV_DDR3_TRAINING_ERR_DFS_H2L; + } + + if ((dram_info.reg_dimm == 1) && + (reg_dimm_skip_wl == 0)) { + if (MV_OK != + ddr3_write_leveling_hw_reg_dimm(freq, + &dram_info)) + DEBUG_MAIN_S("DDR3 Training Sequence - Registered DIMM Low WL - SKIP\n"); + } + + if (ddr3_get_log_level() >= MV_LOG_LEVEL_1) + ddr3_print_freq(freq); + + if (debug_mode) + DEBUG_MAIN_S("DDR3 Training Sequence - DEBUG - 2\n"); + } else { + if (!dqs_clk_aligned) { +#ifdef MV88F67XX + /* + * If running training sequence without DFS, + * we must run Write leveling before writing + * the patterns + */ + + /* + * ODT - Multi CS system use SW WL, + * Single CS System use HW WL + */ + if (dram_info.cs_ena > 1) { + if (MV_OK != + ddr3_write_leveling_sw( + freq, tmp_ratio, + &dram_info)) { + DEBUG_MAIN_S("DDR3 Training Sequence - FAILED (Write Leveling Sw)\n"); + return MV_DDR3_TRAINING_ERR_WR_LVL_SW; + } + } else { + if (MV_OK != + ddr3_write_leveling_hw(freq, + &dram_info)) { + DEBUG_MAIN_S("DDR3 Training Sequence - FAILED (Write Leveling Hw)\n"); + return MV_DDR3_TRAINING_ERR_WR_LVL_HW; + } + } +#else + if (MV_OK != ddr3_write_leveling_hw( + freq, &dram_info)) { + DEBUG_MAIN_S("DDR3 Training Sequence - FAILED (Write Leveling Hw)\n"); + if (ddr3_sw_wl_rl_debug) { + if (MV_OK != + ddr3_write_leveling_sw( + freq, tmp_ratio, + &dram_info)) { + DEBUG_MAIN_S("DDR3 Training Sequence - FAILED (Write Leveling Sw)\n"); + return MV_DDR3_TRAINING_ERR_WR_LVL_SW; + } + } else { + return MV_DDR3_TRAINING_ERR_WR_LVL_HW; + } + } +#endif + } + + if (debug_mode) + DEBUG_MAIN_S("DDR3 Training Sequence - DEBUG - 3\n"); + } + + if (MV_OK != ddr3_load_patterns(&dram_info, 0)) { + DEBUG_MAIN_S("DDR3 Training Sequence - FAILED (Loading Patterns)\n"); + return MV_DDR3_TRAINING_ERR_LOAD_PATTERNS; + } + + /* + * TODO: + * The mainline U-Boot port of the bin_hdr DDR training code + * needs a delay of minimum 20ms here (10ms is a bit too short + * and the CPU hangs). The bin_hdr code doesn't have this delay. + * To be save here, lets add a delay of 50ms here. + * + * Tested on the Marvell DB-MV784MP-GP board + */ + mdelay(50); + + do { + freq = dram_info.target_frequency; + tmp_ratio = ratio_2to1; + DEBUG_MAIN_FULL_S("DDR3 Training Sequence - DEBUG - 4\n"); + +#if defined(MV88F78X60) + /* + * There is a difference on the DFS frequency at the + * first iteration of this loop + */ + if (first_loop_flag) { + freq = DDR_400; + tmp_ratio = 0; + } +#endif + + if (MV_OK != ddr3_dfs_low_2_high(freq, tmp_ratio, + &dram_info)) { + DEBUG_MAIN_S("DDR3 Training Sequence - FAILED (Dfs Low2High)\n"); + return MV_DDR3_TRAINING_ERR_DFS_H2L; + } + + if (ddr3_get_log_level() >= MV_LOG_LEVEL_1) { + ddr3_print_freq(freq); + } + + if (debug_mode) + DEBUG_MAIN_S("DDR3 Training Sequence - DEBUG - 5\n"); + + /* Write leveling */ + if (!dqs_clk_aligned) { +#ifdef MV88F67XX + /* + * ODT - Multi CS system that not support Multi + * CS MRS commands must use SW WL + */ + if (dram_info.cs_ena > 1) { + if (MV_OK != ddr3_write_leveling_sw( + freq, tmp_ratio, &dram_info)) { + DEBUG_MAIN_S("DDR3 Training Sequence - FAILED (Write Leveling Sw)\n"); + return MV_DDR3_TRAINING_ERR_WR_LVL_SW; + } + } else { + if (MV_OK != ddr3_write_leveling_hw( + freq, &dram_info)) { + DEBUG_MAIN_S("DDR3 Training Sequence - FAILED (Write Leveling Hw)\n"); + return MV_DDR3_TRAINING_ERR_WR_LVL_HW; + } + } +#else + if ((dram_info.reg_dimm == 1) && + (freq == DDR_400)) { + if (reg_dimm_skip_wl == 0) { + if (MV_OK != ddr3_write_leveling_hw_reg_dimm( + freq, &dram_info)) + DEBUG_MAIN_S("DDR3 Training Sequence - Registered DIMM WL - SKIP\n"); + } + } else { + if (MV_OK != ddr3_write_leveling_hw( + freq, &dram_info)) { + DEBUG_MAIN_S("DDR3 Training Sequence - FAILED (Write Leveling Hw)\n"); + if (ddr3_sw_wl_rl_debug) { + if (MV_OK != ddr3_write_leveling_sw( + freq, tmp_ratio, &dram_info)) { + DEBUG_MAIN_S("DDR3 Training Sequence - FAILED (Write Leveling Sw)\n"); + return MV_DDR3_TRAINING_ERR_WR_LVL_SW; + } + } else { + return MV_DDR3_TRAINING_ERR_WR_LVL_HW; + } + } + } +#endif + if (debug_mode) + DEBUG_MAIN_S + ("DDR3 Training Sequence - DEBUG - 6\n"); + } + + /* Read Leveling */ + /* + * Armada 370 - Support for HCLK @ 400MHZ - must use + * SW read leveling + */ + if (freq == DDR_400 && dram_info.rl400_bug) { + status = ddr3_read_leveling_sw(freq, tmp_ratio, + &dram_info); + if (MV_OK != status) { + DEBUG_MAIN_S + ("DDR3 Training Sequence - FAILED (Read Leveling Sw)\n"); + return status; + } + } else { + if (MV_OK != ddr3_read_leveling_hw( + freq, &dram_info)) { + DEBUG_MAIN_S("DDR3 Training Sequence - FAILED (Read Leveling Hw)\n"); + if (ddr3_sw_wl_rl_debug) { + if (MV_OK != ddr3_read_leveling_sw( + freq, tmp_ratio, + &dram_info)) { + DEBUG_MAIN_S("DDR3 Training Sequence - FAILED (Read Leveling Sw)\n"); + return MV_DDR3_TRAINING_ERR_WR_LVL_SW; + } + } else { + return MV_DDR3_TRAINING_ERR_WR_LVL_HW; + } + } + } + + if (debug_mode) + DEBUG_MAIN_S("DDR3 Training Sequence - DEBUG - 7\n"); + + if (MV_OK != ddr3_wl_supplement(&dram_info)) { + DEBUG_MAIN_S("DDR3 Training Sequence - FAILED (Write Leveling Hi-Freq Sup)\n"); + return MV_DDR3_TRAINING_ERR_WR_LVL_HI_FREQ; + } + + if (debug_mode) + DEBUG_MAIN_S("DDR3 Training Sequence - DEBUG - 8\n"); +#if !defined(MV88F67XX) + /* A370 has no PBS mechanism */ +#if defined(MV88F78X60) || defined(MV88F672X) + if (first_loop_flag == 1) { + first_loop_flag = 0; + + status = MV_OK; + status = ddr3_pbs_rx(&dram_info); + if (MV_OK != status) { + DEBUG_MAIN_S("DDR3 Training Sequence - FAILED (PBS RX)\n"); + return status; + } + + if (debug_mode) + DEBUG_MAIN_S("DDR3 Training Sequence - DEBUG - 9\n"); + + status = ddr3_pbs_tx(&dram_info); + if (MV_OK != status) { + DEBUG_MAIN_S("DDR3 Training Sequence - FAILED (PBS TX)\n"); + return status; + } + + if (debug_mode) + DEBUG_MAIN_S("DDR3 Training Sequence - DEBUG - 10\n"); + } +#endif +#endif + } while (freq != dram_info.target_frequency); + + status = ddr3_dqs_centralization_rx(&dram_info); + if (MV_OK != status) { + DEBUG_MAIN_S("DDR3 Training Sequence - FAILED (DQS Centralization RX)\n"); + return status; + } + + if (debug_mode) + DEBUG_MAIN_S("DDR3 Training Sequence - DEBUG - 11\n"); + + status = ddr3_dqs_centralization_tx(&dram_info); + if (MV_OK != status) { + DEBUG_MAIN_S("DDR3 Training Sequence - FAILED (DQS Centralization TX)\n"); + return status; + } + + if (debug_mode) + DEBUG_MAIN_S("DDR3 Training Sequence - DEBUG - 12\n"); + } + + ddr3_set_performance_params(&dram_info); + + if (dram_info.ecc_ena) { + /* Need to SCRUB the DRAM memory area to load U-boot */ + mv_sys_xor_finish(); + dram_info.num_cs = 1; + dram_info.cs_ena = 1; + mv_sys_xor_init(&dram_info); + mv_xor_mem_init(0, scrub_offs, scrub_size, 0xdeadbeef, + 0xdeadbeef); + + /* Wait for previous transfer completion */ + while (mv_xor_state_get(0) != MV_IDLE) + ; + + if (debug_mode) + DEBUG_MAIN_S("DDR3 Training Sequence - DEBUG - 13\n"); + } + + /* Return XOR State */ + mv_sys_xor_finish(); + +#if defined(MV88F78X60) + /* Save training results in memeory for resume state */ + ddr3_save_training(&dram_info); +#endif + /* Clear ODT always on */ + ddr3_odt_activate(0); + + /* Configure Dynamic read ODT */ + ddr3_odt_read_dynamic_config(&dram_info); + + return MV_OK; +} + +void ddr3_set_performance_params(MV_DRAM_INFO *dram_info) +{ + u32 twr2wr, trd2rd, trd2wr_wr2rd; + u32 tmp1, tmp2, reg; + + DEBUG_MAIN_FULL_C("Max WL Phase: ", dram_info->wl_max_phase, 2); + DEBUG_MAIN_FULL_C("Min WL Phase: ", dram_info->wl_min_phase, 2); + DEBUG_MAIN_FULL_C("Max RL Phase: ", dram_info->rl_max_phase, 2); + DEBUG_MAIN_FULL_C("Min RL Phase: ", dram_info->rl_min_phase, 2); + + if (dram_info->wl_max_phase < 2) + twr2wr = 0x2; + else + twr2wr = 0x3; + + trd2rd = 0x1 + (dram_info->rl_max_phase + 1) / 2 + + (dram_info->rl_max_phase + 1) % 2; + + tmp1 = (dram_info->rl_max_phase - dram_info->wl_min_phase) / 2 + + (((dram_info->rl_max_phase - dram_info->wl_min_phase) % 2) > + 0 ? 1 : 0); + tmp2 = (dram_info->wl_max_phase - dram_info->rl_min_phase) / 2 + + ((dram_info->wl_max_phase - dram_info->rl_min_phase) % 2 > + 0 ? 1 : 0); + trd2wr_wr2rd = (tmp1 >= tmp2) ? tmp1 : tmp2; + + trd2wr_wr2rd += 2; + trd2rd += 2; + twr2wr += 2; + + DEBUG_MAIN_FULL_C("WR 2 WR: ", twr2wr, 2); + DEBUG_MAIN_FULL_C("RD 2 RD: ", trd2rd, 2); + DEBUG_MAIN_FULL_C("RD 2 WR / WR 2 RD: ", trd2wr_wr2rd, 2); + + reg = reg_read(REG_SDRAM_TIMING_HIGH_ADDR); + + reg &= ~(REG_SDRAM_TIMING_H_W2W_MASK << REG_SDRAM_TIMING_H_W2W_OFFS); + reg |= ((twr2wr & REG_SDRAM_TIMING_H_W2W_MASK) << + REG_SDRAM_TIMING_H_W2W_OFFS); + + reg &= ~(REG_SDRAM_TIMING_H_R2R_MASK << REG_SDRAM_TIMING_H_R2R_OFFS); + reg &= ~(REG_SDRAM_TIMING_H_R2R_H_MASK << + REG_SDRAM_TIMING_H_R2R_H_OFFS); + reg |= ((trd2rd & REG_SDRAM_TIMING_H_R2R_MASK) << + REG_SDRAM_TIMING_H_R2R_OFFS); + reg |= (((trd2rd >> 2) & REG_SDRAM_TIMING_H_R2R_H_MASK) << + REG_SDRAM_TIMING_H_R2R_H_OFFS); + + reg &= ~(REG_SDRAM_TIMING_H_R2W_W2R_MASK << + REG_SDRAM_TIMING_H_R2W_W2R_OFFS); + reg &= ~(REG_SDRAM_TIMING_H_R2W_W2R_H_MASK << + REG_SDRAM_TIMING_H_R2W_W2R_H_OFFS); + reg |= ((trd2wr_wr2rd & REG_SDRAM_TIMING_H_R2W_W2R_MASK) << + REG_SDRAM_TIMING_H_R2W_W2R_OFFS); + reg |= (((trd2wr_wr2rd >> 2) & REG_SDRAM_TIMING_H_R2W_W2R_H_MASK) << + REG_SDRAM_TIMING_H_R2W_W2R_H_OFFS); + + reg_write(REG_SDRAM_TIMING_HIGH_ADDR, reg); +} + +/* + * Perform DDR3 PUP Indirect Write + */ +void ddr3_write_pup_reg(u32 mode, u32 cs, u32 pup, u32 phase, u32 delay) +{ + u32 reg = 0; + + if (pup == PUP_BC) + reg |= (1 << REG_PHY_BC_OFFS); + else + reg |= (pup << REG_PHY_PUP_OFFS); + + reg |= ((0x4 * cs + mode) << REG_PHY_CS_OFFS); + reg |= (phase << REG_PHY_PHASE_OFFS) | delay; + + if (mode == PUP_WL_MODE) + reg |= ((INIT_WL_DELAY + delay) << REG_PHY_DQS_REF_DLY_OFFS); + + reg_write(REG_PHY_REGISTRY_FILE_ACCESS_ADDR, reg); /* 0x16A0 */ + reg |= REG_PHY_REGISTRY_FILE_ACCESS_OP_WR; + reg_write(REG_PHY_REGISTRY_FILE_ACCESS_ADDR, reg); /* 0x16A0 */ + + do { + reg = reg_read(REG_PHY_REGISTRY_FILE_ACCESS_ADDR) & + REG_PHY_REGISTRY_FILE_ACCESS_OP_DONE; + } while (reg); /* Wait for '0' to mark the end of the transaction */ + + /* If read Leveling mode - need to write to register 3 separetly */ + if (mode == PUP_RL_MODE) { + reg = 0; + + if (pup == PUP_BC) + reg |= (1 << REG_PHY_BC_OFFS); + else + reg |= (pup << REG_PHY_PUP_OFFS); + + reg |= ((0x4 * cs + mode + 1) << REG_PHY_CS_OFFS); + reg |= (INIT_RL_DELAY); + + reg_write(REG_PHY_REGISTRY_FILE_ACCESS_ADDR, reg); /* 0x16A0 */ + reg |= REG_PHY_REGISTRY_FILE_ACCESS_OP_WR; + reg_write(REG_PHY_REGISTRY_FILE_ACCESS_ADDR, reg); /* 0x16A0 */ + + do { + reg = reg_read(REG_PHY_REGISTRY_FILE_ACCESS_ADDR) & + REG_PHY_REGISTRY_FILE_ACCESS_OP_DONE; + } while (reg); + } +} + +/* + * Perform DDR3 PUP Indirect Read + */ +u32 ddr3_read_pup_reg(u32 mode, u32 cs, u32 pup) +{ + u32 reg; + + reg = (pup << REG_PHY_PUP_OFFS) | + ((0x4 * cs + mode) << REG_PHY_CS_OFFS); + reg_write(REG_PHY_REGISTRY_FILE_ACCESS_ADDR, reg); /* 0x16A0 */ + + reg |= REG_PHY_REGISTRY_FILE_ACCESS_OP_RD; + reg_write(REG_PHY_REGISTRY_FILE_ACCESS_ADDR, reg); /* 0x16A0 */ + + do { + reg = reg_read(REG_PHY_REGISTRY_FILE_ACCESS_ADDR) & + REG_PHY_REGISTRY_FILE_ACCESS_OP_DONE; + } while (reg); /* Wait for '0' to mark the end of the transaction */ + + return reg_read(REG_PHY_REGISTRY_FILE_ACCESS_ADDR); /* 0x16A0 */ +} + +/* + * Set training patterns + */ +int ddr3_load_patterns(MV_DRAM_INFO *dram_info, int resume) +{ + u32 reg; + + /* Enable SW override - Required for the ECC Pup */ + reg = reg_read(REG_DRAM_TRAINING_2_ADDR) | + (1 << REG_DRAM_TRAINING_2_SW_OVRD_OFFS); + + /* [0] = 1 - Enable SW override */ + /* 0x15B8 - Training SW 2 Register */ + reg_write(REG_DRAM_TRAINING_2_ADDR, reg); + + reg = (1 << REG_DRAM_TRAINING_AUTO_OFFS); + reg_write(REG_DRAM_TRAINING_ADDR, reg); /* 0x15B0 - Training Register */ + + if (resume == 0) { +#if defined(MV88F78X60) || defined(MV88F672X) + ddr3_load_pbs_patterns(dram_info); +#endif + ddr3_load_dqs_patterns(dram_info); + } + + /* Disable SW override - Must be in a different stage */ + /* [0]=0 - Enable SW override */ + reg = reg_read(REG_DRAM_TRAINING_2_ADDR); + reg &= ~(1 << REG_DRAM_TRAINING_2_SW_OVRD_OFFS); + /* 0x15B8 - Training SW 2 Register */ + reg_write(REG_DRAM_TRAINING_2_ADDR, reg); + + reg = reg_read(REG_DRAM_TRAINING_1_ADDR) | + (1 << REG_DRAM_TRAINING_1_TRNBPOINT_OFFS); + reg_write(REG_DRAM_TRAINING_1_ADDR, reg); + + /* Set Base Addr */ +#if defined(MV88F67XX) + reg_write(REG_DRAM_TRAINING_PATTERN_BASE_ADDR, 0); +#else + if (resume == 0) + reg_write(REG_DRAM_TRAINING_PATTERN_BASE_ADDR, 0); + else + reg_write(REG_DRAM_TRAINING_PATTERN_BASE_ADDR, + RESUME_RL_PATTERNS_ADDR); +#endif + + /* Set Patterns */ + if (resume == 0) { + reg = (dram_info->cs_ena << REG_DRAM_TRAINING_CS_OFFS) | + (1 << REG_DRAM_TRAINING_PATTERNS_OFFS); + } else { + reg = (0x1 << REG_DRAM_TRAINING_CS_OFFS) | + (1 << REG_DRAM_TRAINING_PATTERNS_OFFS); + } + + reg |= (1 << REG_DRAM_TRAINING_AUTO_OFFS); + + reg_write(REG_DRAM_TRAINING_ADDR, reg); + + udelay(100); + + /* Check if Successful */ + if (reg_read(REG_DRAM_TRAINING_ADDR) & + (1 << REG_DRAM_TRAINING_ERROR_OFFS)) + return MV_OK; + else + return MV_FAIL; +} + +#if !defined(MV88F67XX) +/* + * Name: ddr3_save_training(MV_DRAM_INFO *dram_info) + * Desc: saves the training results to memeory (RL,WL,PBS,Rx/Tx + * Centeralization) + * Args: MV_DRAM_INFO *dram_info + * Notes: + * Returns: None. + */ +void ddr3_save_training(MV_DRAM_INFO *dram_info) +{ + u32 val, pup, tmp_cs, cs, i, dq; + u32 crc = 0; + u32 regs = 0; + u32 *sdram_offset = (u32 *)RESUME_TRAINING_VALUES_ADDR; + u32 mode_config[MAX_TRAINING_MODE]; + + mode_config[DQS_WR_MODE] = PUP_DQS_WR; + mode_config[WL_MODE_] = PUP_WL_MODE; + mode_config[RL_MODE_] = PUP_RL_MODE; + mode_config[DQS_RD_MODE] = PUP_DQS_RD; + mode_config[PBS_TX_DM_MODE] = PUP_PBS_TX_DM; + mode_config[PBS_TX_MODE] = PUP_PBS_TX; + mode_config[PBS_RX_MODE] = PUP_PBS_RX; + + /* num of training modes */ + for (i = 0; i < MAX_TRAINING_MODE; i++) { + tmp_cs = dram_info->cs_ena; + /* num of CS */ + for (cs = 0; cs < MAX_CS; cs++) { + if (tmp_cs & (1 << cs)) { + /* num of PUPs */ + for (pup = 0; pup < dram_info->num_of_total_pups; + pup++) { + if (pup == dram_info->num_of_std_pups && + dram_info->ecc_ena) + pup = ECC_PUP; + if (i == PBS_TX_DM_MODE) { + /* + * Change CS bitmask because + * PBS works only with CS0 + */ + tmp_cs = 0x1; + val = ddr3_read_pup_reg( + mode_config[i], CS0, pup); + } else if (i == PBS_TX_MODE || + i == PBS_RX_MODE) { + /* + * Change CS bitmask because + * PBS works only with CS0 + */ + tmp_cs = 0x1; + for (dq = 0; dq <= DQ_NUM; + dq++) { + val = ddr3_read_pup_reg( + mode_config[i] + dq, + CS0, + pup); + (*sdram_offset) = val; + crc += *sdram_offset; + sdram_offset++; + regs++; + } + continue; + } else { + val = ddr3_read_pup_reg( + mode_config[i], cs, pup); + } + + *sdram_offset = val; + crc += *sdram_offset; + sdram_offset++; + regs++; + } + } + } + } + + *sdram_offset = reg_read(REG_READ_DATA_SAMPLE_DELAYS_ADDR); + crc += *sdram_offset; + sdram_offset++; + regs++; + *sdram_offset = reg_read(REG_READ_DATA_READY_DELAYS_ADDR); + crc += *sdram_offset; + sdram_offset++; + regs++; + sdram_offset = (u32 *)NUM_OF_REGISTER_ADDR; + *sdram_offset = regs; + DEBUG_SUSPEND_RESUME_S("Training Results CheckSum write= "); + DEBUG_SUSPEND_RESUME_D(crc, 8); + DEBUG_SUSPEND_RESUME_S("\n"); + sdram_offset = (u32 *)CHECKSUM_RESULT_ADDR; + *sdram_offset = crc; +} + +/* + * Name: ddr3_read_training_results() + * Desc: Reads the training results from memeory (RL,WL,PBS,Rx/Tx + * Centeralization) + * and writes them to the relevant registers + * Args: MV_DRAM_INFO *dram_info + * Notes: + * Returns: None. + */ +int ddr3_read_training_results(void) +{ + u32 val, reg, idx, dqs_wr_idx = 0, crc = 0; + u32 *sdram_offset = (u32 *)RESUME_TRAINING_VALUES_ADDR; + u32 training_val[RESUME_TRAINING_VALUES_MAX] = { 0 }; + u32 regs = *((u32 *)NUM_OF_REGISTER_ADDR); + + /* + * Read Training results & Dunit registers from memory and write + * it to an array + */ + for (idx = 0; idx < regs; idx++) { + training_val[idx] = *sdram_offset; + crc += *sdram_offset; + sdram_offset++; + } + + sdram_offset = (u32 *)CHECKSUM_RESULT_ADDR; + + if ((*sdram_offset) == crc) { + DEBUG_SUSPEND_RESUME_S("Training Results CheckSum read PASS= "); + DEBUG_SUSPEND_RESUME_D(crc, 8); + DEBUG_SUSPEND_RESUME_S("\n"); + } else { + DEBUG_MAIN_S("Wrong Training Results CheckSum\n"); + return MV_FAIL; + } + + /* + * We iterate through all the registers except for the last 2 since + * they are Dunit registers (and not PHY registers) + */ + for (idx = 0; idx < (regs - 2); idx++) { + val = training_val[idx]; + reg = (val >> REG_PHY_CS_OFFS) & 0x3F; /*read the phy address */ + + /* Check if the values belongs to the DQS WR */ + if (reg == PUP_WL_MODE) { + /* bit[5:0] in DQS_WR are delay */ + val = (training_val[dqs_wr_idx++] & 0x3F); + /* + * bit[15:10] are DQS_WR delay & bit[9:0] are + * WL phase & delay + */ + val = (val << REG_PHY_DQS_REF_DLY_OFFS) | + (training_val[idx] & 0x3C003FF); + /* Add Request pending and write operation bits */ + val |= REG_PHY_REGISTRY_FILE_ACCESS_OP_WR; + } else if (reg == PUP_DQS_WR) { + /* + * Do nothing since DQS_WR will be done in PUP_WL_MODE + */ + continue; + } + + val |= REG_PHY_REGISTRY_FILE_ACCESS_OP_WR; + reg_write(REG_PHY_REGISTRY_FILE_ACCESS_ADDR, val); + do { + val = (reg_read(REG_PHY_REGISTRY_FILE_ACCESS_ADDR)) & + REG_PHY_REGISTRY_FILE_ACCESS_OP_DONE; + } while (val); /* Wait for '0' to mark the end of the transaction */ + } + + /* write last 2 Dunit configurations */ + val = training_val[idx]; + reg_write(REG_READ_DATA_SAMPLE_DELAYS_ADDR, val); /* reg 0x1538 */ + val = training_val[idx + 1]; + reg_write(REG_READ_DATA_READY_DELAYS_ADDR, val); /* reg 0x153c */ + + return MV_OK; +} + +/* + * Name: ddr3_check_if_resume_mode() + * Desc: Reads the address (0x3000) of the Resume Magic word (0xDEADB002) + * Args: MV_DRAM_INFO *dram_info + * Notes: + * Returns: return (magic_word == SUSPEND_MAGIC_WORD) + */ +int ddr3_check_if_resume_mode(MV_DRAM_INFO *dram_info, u32 freq) +{ + u32 magic_word; + u32 *sdram_offset = (u32 *)BOOT_INFO_ADDR; + + if (dram_info->reg_dimm != 1) { + /* + * Perform write levleling in order initiate the phy with + * low frequency + */ + if (MV_OK != ddr3_write_leveling_hw(freq, dram_info)) { + DEBUG_MAIN_S("DDR3 Training Sequence - FAILED (Write Leveling Hw)\n"); + return MV_DDR3_TRAINING_ERR_WR_LVL_HW; + } + } + + if (MV_OK != ddr3_load_patterns(dram_info, 1)) { + DEBUG_MAIN_S("DDR3 Training Sequence - FAILED (Loading Patterns)\n"); + return MV_DDR3_TRAINING_ERR_LOAD_PATTERNS; + } + + /* Enable CS0 only for RL */ + dram_info->cs_ena = 0x1; + + /* Perform Read levleling in order to get stable memory */ + if (MV_OK != ddr3_read_leveling_hw(freq, dram_info)) { + DEBUG_MAIN_S("DDR3 Training Sequence - FAILED (Read Leveling Hw)\n"); + return MV_DDR3_TRAINING_ERR_WR_LVL_HW; + } + + /* Back to relevant CS */ + dram_info->cs_ena = ddr3_get_cs_ena_from_reg(); + + magic_word = *sdram_offset; + return magic_word == SUSPEND_MAGIC_WORD; +} + +/* + * Name: ddr3_training_suspend_resume() + * Desc: Execute the Resume state + * Args: MV_DRAM_INFO *dram_info + * Notes: + * Returns: return (magic_word == SUSPEND_MAGIC_WORD) + */ +int ddr3_training_suspend_resume(MV_DRAM_INFO *dram_info) +{ + u32 freq, reg; + int tmp_ratio; + + /* Configure DDR */ + if (MV_OK != ddr3_read_training_results()) + return MV_FAIL; + + /* Reset read FIFO */ + reg = reg_read(REG_DRAM_TRAINING_ADDR); + + /* Start Auto Read Leveling procedure */ + reg |= (1 << REG_DRAM_TRAINING_RL_OFFS); + reg_write(REG_DRAM_TRAINING_ADDR, reg); /* 0x15B0 - Training Register */ + + reg = reg_read(REG_DRAM_TRAINING_2_ADDR); + reg |= ((1 << REG_DRAM_TRAINING_2_FIFO_RST_OFFS) + + (1 << REG_DRAM_TRAINING_2_SW_OVRD_OFFS)); + + /* [0] = 1 - Enable SW override, [4] = 1 - FIFO reset */ + /* 0x15B8 - Training SW 2 Register */ + reg_write(REG_DRAM_TRAINING_2_ADDR, reg); + + udelay(2); + + reg = reg_read(REG_DRAM_TRAINING_ADDR); + /* Clear Auto Read Leveling procedure */ + reg &= ~(1 << REG_DRAM_TRAINING_RL_OFFS); + reg_write(REG_DRAM_TRAINING_ADDR, reg); /* 0x15B0 - Training Register */ + + /* Return to target frequency */ + freq = dram_info->target_frequency; + tmp_ratio = 1; + if (MV_OK != ddr3_dfs_low_2_high(freq, tmp_ratio, dram_info)) { + DEBUG_MAIN_S("DDR3 Training Sequence - FAILED (Dfs Low2High)\n"); + return MV_DDR3_TRAINING_ERR_DFS_H2L; + } + + if (dram_info->ecc_ena) { + /* Scabbling the RL area pattern and the training area */ + mv_sys_xor_finish(); + dram_info->num_cs = 1; + dram_info->cs_ena = 1; + mv_sys_xor_init(dram_info); + mv_xor_mem_init(0, RESUME_RL_PATTERNS_ADDR, + RESUME_RL_PATTERNS_SIZE, 0xFFFFFFFF, 0xFFFFFFFF); + + /* Wait for previous transfer completion */ + + while (mv_xor_state_get(0) != MV_IDLE) + ; + + /* Return XOR State */ + mv_sys_xor_finish(); + } + + return MV_OK; +} +#endif + +void ddr3_print_freq(u32 freq) +{ + u32 tmp_freq; + + switch (freq) { + case 0: + tmp_freq = 100; + break; + case 1: + tmp_freq = 300; + break; + case 2: + tmp_freq = 360; + break; + case 3: + tmp_freq = 400; + break; + case 4: + tmp_freq = 444; + break; + case 5: + tmp_freq = 500; + break; + case 6: + tmp_freq = 533; + break; + case 7: + tmp_freq = 600; + break; + case 8: + tmp_freq = 666; + break; + case 9: + tmp_freq = 720; + break; + case 10: + tmp_freq = 800; + break; + default: + tmp_freq = 100; + } + + printf("Current frequency is: %dMHz\n", tmp_freq); +} + +int ddr3_get_min_max_read_sample_delay(u32 cs_enable, u32 reg, u32 *min, + u32 *max, u32 *cs_max) +{ + u32 cs, delay; + + *min = 0xFFFFFFFF; + *max = 0x0; + + for (cs = 0; cs < MAX_CS; cs++) { + if ((cs_enable & (1 << cs)) == 0) + continue; + + delay = ((reg >> (cs * 8)) & 0x1F); + + if (delay < *min) + *min = delay; + + if (delay > *max) { + *max = delay; + *cs_max = cs; + } + } + + return MV_OK; +} + +int ddr3_get_min_max_rl_phase(MV_DRAM_INFO *dram_info, u32 *min, u32 *max, + u32 cs) +{ + u32 pup, reg, phase; + + *min = 0xFFFFFFFF; + *max = 0x0; + + for (pup = 0; pup < dram_info->num_of_total_pups; pup++) { + reg = ddr3_read_pup_reg(PUP_RL_MODE, cs, pup); + phase = ((reg >> 8) & 0x7); + + if (phase < *min) + *min = phase; + + if (phase > *max) + *max = phase; + } + + return MV_OK; +} + +int ddr3_odt_activate(int activate) +{ + u32 reg, mask; + + mask = (1 << REG_DUNIT_ODT_CTRL_OVRD_OFFS) | + (1 << REG_DUNIT_ODT_CTRL_OVRD_VAL_OFFS); + /* {0x0000149C} - DDR Dunit ODT Control Register */ + reg = reg_read(REG_DUNIT_ODT_CTRL_ADDR); + if (activate) + reg |= mask; + else + reg &= ~mask; + + reg_write(REG_DUNIT_ODT_CTRL_ADDR, reg); + + return MV_OK; +} + +int ddr3_odt_read_dynamic_config(MV_DRAM_INFO *dram_info) +{ + u32 min_read_sample_delay, max_read_sample_delay, max_rl_phase; + u32 min, max, cs_max; + u32 cs_ena, reg; + + reg = reg_read(REG_READ_DATA_SAMPLE_DELAYS_ADDR); + cs_ena = ddr3_get_cs_ena_from_reg(); + + /* Get minimum and maximum of read sample delay of all CS */ + ddr3_get_min_max_read_sample_delay(cs_ena, reg, &min_read_sample_delay, + &max_read_sample_delay, &cs_max); + + /* + * Get minimum and maximum read leveling phase which belongs to the + * maximal read sample delay + */ + ddr3_get_min_max_rl_phase(dram_info, &min, &max, cs_max); + max_rl_phase = max; + + /* DDR ODT Timing (Low) Register calculation */ + reg = reg_read(REG_ODT_TIME_LOW_ADDR); + reg &= ~(0x1FF << REG_ODT_ON_CTL_RD_OFFS); + reg |= (((min_read_sample_delay - 1) & 0xF) << REG_ODT_ON_CTL_RD_OFFS); + reg |= (((max_read_sample_delay + 4 + (((max_rl_phase + 1) / 2) + 1)) & + 0x1F) << REG_ODT_OFF_CTL_RD_OFFS); + reg_write(REG_ODT_TIME_LOW_ADDR, reg); + + return MV_OK; +} diff --git a/drivers/ddr/mvebu/ddr3_hw_training.h b/drivers/ddr/mvebu/ddr3_hw_training.h new file mode 100644 index 0000000..cffa7c4 --- /dev/null +++ b/drivers/ddr/mvebu/ddr3_hw_training.h @@ -0,0 +1,392 @@ +/* + * Copyright (C) Marvell International Ltd. and its affiliates + * + * SPDX-License-Identifier: GPL-2.0 + */ + +#ifndef __DDR3_TRAINING_H +#define __DDR3_TRAINING_H + +#include "ddr3_init.h" + +#ifdef MV88F78X60 +#include "ddr3_axp.h" +#elif defined(MV88F67XX) +#include "ddr3_a370.h" +#elif defined(MV88F672X) +#include "ddr3_a375.h" +#endif + +/* The following is a list of Marvell status */ +#define MV_ERROR (-1) +#define MV_OK (0x00) /* Operation succeeded */ +#define MV_FAIL (0x01) /* Operation failed */ +#define MV_BAD_VALUE (0x02) /* Illegal value (general) */ +#define MV_OUT_OF_RANGE (0x03) /* The value is out of range */ +#define MV_BAD_PARAM (0x04) /* Illegal parameter in function called */ +#define MV_BAD_PTR (0x05) /* Illegal pointer value */ +#define MV_BAD_SIZE (0x06) /* Illegal size */ +#define MV_BAD_STATE (0x07) /* Illegal state of state machine */ +#define MV_SET_ERROR (0x08) /* Set operation failed */ +#define MV_GET_ERROR (0x09) /* Get operation failed */ +#define MV_CREATE_ERROR (0x0A) /* Fail while creating an item */ +#define MV_NOT_FOUND (0x0B) /* Item not found */ +#define MV_NO_MORE (0x0C) /* No more items found */ +#define MV_NO_SUCH (0x0D) /* No such item */ +#define MV_TIMEOUT (0x0E) /* Time Out */ +#define MV_NO_CHANGE (0x0F) /* Parameter(s) is already in this value */ +#define MV_NOT_SUPPORTED (0x10) /* This request is not support */ +#define MV_NOT_IMPLEMENTED (0x11) /* Request supported but not implemented*/ +#define MV_NOT_INITIALIZED (0x12) /* The item is not initialized */ +#define MV_NO_RESOURCE (0x13) /* Resource not available (memory ...) */ +#define MV_FULL (0x14) /* Item is full (Queue or table etc...) */ +#define MV_EMPTY (0x15) /* Item is empty (Queue or table etc...) */ +#define MV_INIT_ERROR (0x16) /* Error occured while INIT process */ +#define MV_HW_ERROR (0x17) /* Hardware error */ +#define MV_TX_ERROR (0x18) /* Transmit operation not succeeded */ +#define MV_RX_ERROR (0x19) /* Recieve operation not succeeded */ +#define MV_NOT_READY (0x1A) /* The other side is not ready yet */ +#define MV_ALREADY_EXIST (0x1B) /* Tried to create existing item */ +#define MV_OUT_OF_CPU_MEM (0x1C) /* Cpu memory allocation failed. */ +#define MV_NOT_STARTED (0x1D) /* Not started yet */ +#define MV_BUSY (0x1E) /* Item is busy. */ +#define MV_TERMINATE (0x1F) /* Item terminates it's work. */ +#define MV_NOT_ALIGNED (0x20) /* Wrong alignment */ +#define MV_NOT_ALLOWED (0x21) /* Operation NOT allowed */ +#define MV_WRITE_PROTECT (0x22) /* Write protected */ + +#define MV_INVALID (int)(-1) + +/* + * Debug (Enable/Disable modules) and Error report + */ + +#ifdef BASIC_DEBUG +#define MV_DEBUG_WL +#define MV_DEBUG_RL +#define MV_DEBUG_DQS_RESULTS +#endif + +#ifdef FULL_DEBUG +#define MV_DEBUG_WL +#define MV_DEBUG_RL +#define MV_DEBUG_DQS + +#define MV_DEBUG_PBS +#define MV_DEBUG_DFS +#define MV_DEBUG_MAIN_FULL +#define MV_DEBUG_DFS_FULL +#define MV_DEBUG_DQS_FULL +#define MV_DEBUG_RL_FULL +#define MV_DEBUG_WL_FULL +#endif + +/* + * General Consts + */ + +#define SDRAM_READ_WRITE_LEN_IN_WORDS 16 +#define SDRAM_READ_WRITE_LEN_IN_DOUBLE_WORDS 8 +#define CACHE_LINE_SIZE 0x20 + +#define SDRAM_CS_BASE 0x0 + +#define SRAM_BASE 0x40000000 +#define SRAM_SIZE 0xFFF + +#define LEN_64BIT_STD_PATTERN 16 +#define LEN_64BIT_KILLER_PATTERN 128 +#define LEN_64BIT_SPECIAL_PATTERN 128 +#define LEN_64BIT_PBS_PATTERN 16 +#define LEN_WL_SUP_PATTERN 32 + +#define LEN_16BIT_STD_PATTERN 4 +#define LEN_16BIT_KILLER_PATTERN 128 +#define LEN_16BIT_SPECIAL_PATTERN 128 +#define LEN_16BIT_PBS_PATTERN 4 + +#define CMP_BYTE_SHIFT 8 +#define CMP_BYTE_MASK 0xFF +#define PUP_SIZE 8 + +#define S 0 +#define C 1 +#define P 2 +#define D 3 +#define DQS 6 +#define PS 2 +#define DS 3 +#define PE 4 +#define DE 5 + +#define CS0 0 +#define MAX_DIMM_NUM 2 +#define MAX_DELAY 0x1F + +/* + * Invertion limit and phase1 limit are WA for the RL @ 1:1 design bug - + * Armada 370 & AXP Z1 + */ +#define MAX_DELAY_INV_LIMIT 0x5 +#define MIN_DELAY_PHASE_1_LIMIT 0x10 + +#define MAX_DELAY_INV (0x3F - MAX_DELAY_INV_LIMIT) +#define MIN_DELAY 0 +#define MAX_PUP_NUM 9 +#define ECC_PUP 8 +#define DQ_NUM 8 +#define DQS_DQ_NUM 8 +#define INIT_WL_DELAY 13 +#define INIT_RL_DELAY 15 +#define TWLMRD_DELAY 20 +#define TCLK_3_DELAY 3 +#define ECC_BIT 8 +#define DMA_SIZE 64 +#define MV_DMA_0 0 +#define MAX_TRAINING_RETRY 10 + +#define PUP_RL_MODE 0x2 +#define PUP_WL_MODE 0 +#define PUP_PBS_TX 0x10 +#define PUP_PBS_TX_DM 0x1A +#define PUP_PBS_RX 0x30 +#define PUP_DQS_WR 0x1 +#define PUP_DQS_RD 0x3 +#define PUP_BC 10 +#define PUP_DELAY_MASK 0x1F +#define PUP_PHASE_MASK 0x7 +#define PUP_NUM_64BIT 8 +#define PUP_NUM_32BIT 4 +#define PUP_NUM_16BIT 2 + +/* control PHY registers */ +#define CNTRL_PUP_DESKEW 0x10 + +/* WL */ +#define COUNT_WL_HI_FREQ 2 +#define COUNT_WL 2 +#define COUNT_WL_RFRS 9 +#define WL_HI_FREQ_SHIFT 2 +#define WL_HI_FREQ_STATE 1 +#define COUNT_HW_WL 2 + +/* RL */ +/* + * RL_MODE - this define uses the RL mode SW RL instead of the functional + * window SW RL + */ +#define RL_MODE +#define RL_WINDOW_WA +#define MAX_PHASE_1TO1 2 +#define MAX_PHASE_2TO1 4 + +#define MAX_PHASE_RL_UL_1TO1 0 +#define MAX_PHASE_RL_L_1TO1 4 +#define MAX_PHASE_RL_UL_2TO1 3 +#define MAX_PHASE_RL_L_2TO1 7 + +#define RL_UNLOCK_STATE 0 +#define RL_WINDOW_STATE 1 +#define RL_FINAL_STATE 2 +#define RL_RETRY_COUNT 2 +#define COUNT_HW_RL 2 + +/* PBS */ +#define MAX_PBS 31 +#define MIN_PBS 0 +#define COUNT_PBS_PATTERN 2 +#define COUNT_PBS_STARTOVER 2 +#define COUNT_PBS_REPEAT 3 +#define COUNT_PBS_COMP_RETRY_NUM 2 +#define PBS_DIFF_LIMIT 31 +#define PATTERN_PBS_TX_A 0x55555555 +#define PATTERN_PBS_TX_B 0xAAAAAAAA + +/* DQS */ +#define ADLL_ERROR 0x55 +#define ADLL_MAX 31 +#define ADLL_MIN 0 +#define MIN_WIN_SIZE 4 +#define VALID_WIN_THRS MIN_WIN_SIZE + +#define MODE_2TO1 1 +#define MODE_1TO1 0 + +/* + * Macros + */ +#define IS_PUP_ACTIVE(_data_, _pup_) (((_data_) >> (_pup_)) & 0x1) + +/* + * Internal ERROR codes + */ +#define MV_DDR3_TRAINING_ERR_WR_LVL_HW 0xDD302001 +#define MV_DDR3_TRAINING_ERR_LOAD_PATTERNS 0xDD302002 +#define MV_DDR3_TRAINING_ERR_WR_LVL_HI_FREQ 0xDD302003 +#define MV_DDR3_TRAINING_ERR_DFS_H2L 0xDD302004 +#define MV_DDR3_TRAINING_ERR_DRAM_COMPARE 0xDD302005 +#define MV_DDR3_TRAINING_ERR_WIN_LIMITS 0xDD302006 +#define MV_DDR3_TRAINING_ERR_PUP_RANGE 0xDD302025 +#define MV_DDR3_TRAINING_ERR_DQS_LOW_LIMIT_SEARCH 0xDD302007 +#define MV_DDR3_TRAINING_ERR_DQS_HIGH_LIMIT_SEARCH 0xDD302008 +#define MV_DDR3_TRAINING_ERR_DQS_PATTERN 0xDD302009 +#define MV_DDR3_TRAINING_ERR_PBS_ADLL_SHR_1PHASE 0xDD302010 +#define MV_DDR3_TRAINING_ERR_PBS_TX_MAX_VAL 0xDD302011 +#define MV_DDR3_TRAINING_ERR_PBS_RX_PER_BIT 0xDD302012 +#define MV_DDR3_TRAINING_ERR_PBS_TX_PER_BIT 0xDD302013 +#define MV_DDR3_TRAINING_ERR_PBS_RX_MAX_VAL 0xDD302014 +#define MV_DDR3_TRAINING_ERR_PBS_SHIFT_QDS_SRAM_CMP 0xDD302015 +#define MV_DDR3_TRAINING_ERR_PBS_SHIFT_QDS_MAX_VAL 0xDD302016 +#define MV_DDR3_TRAINING_ERR_RD_LVL_RL_PATTERN 0xDD302017 +#define MV_DDR3_TRAINING_ERR_RD_LVL_RL_PUP_UNLOCK 0xDD302018 +#define MV_DDR3_TRAINING_ERR_RD_LVL_PUP_UNLOCK 0xDD302019 +#define MV_DDR3_TRAINING_ERR_WR_LVL_SW 0xDD302020 +#define MV_DDR3_TRAINING_ERR_PRBS_RX 0xDD302021 +#define MV_DDR3_TRAINING_ERR_DQS_RX 0xDD302022 +#define MV_DDR3_TRAINING_ERR_PRBS_TX 0xDD302023 +#define MV_DDR3_TRAINING_ERR_DQS_TX 0xDD302024 + +/* + * DRAM information structure + */ +typedef struct dram_info { + u32 num_cs; + u32 cs_ena; + u32 num_of_std_pups; /* Q value = ddrWidth/8 - Without ECC!! */ + u32 num_of_total_pups; /* numOfStdPups + eccEna */ + u32 target_frequency; /* DDR Frequency */ + u32 ddr_width; /* 32/64 Bit or 16/32 Bit */ + u32 ecc_ena; /* 0/1 */ + u32 wl_val[MAX_CS][MAX_PUP_NUM][7]; + u32 rl_val[MAX_CS][MAX_PUP_NUM][7]; + u32 rl_max_phase; + u32 rl_min_phase; + u32 wl_max_phase; + u32 wl_min_phase; + u32 rd_smpl_dly; + u32 rd_rdy_dly; + u32 cl; + u32 cwl; + u32 mode_2t; + int rl400_bug; + int multi_cs_mr_support; + int reg_dimm; +} MV_DRAM_INFO; + +enum training_modes { + DQS_WR_MODE, + WL_MODE_, + RL_MODE_, + DQS_RD_MODE, + PBS_TX_DM_MODE, + PBS_TX_MODE, + PBS_RX_MODE, + MAX_TRAINING_MODE, +}; + +typedef struct dram_training_init { + u32 reg_addr; + u32 reg_value; +} MV_DRAM_TRAINING_INIT; + +typedef struct dram_mv_init { + u32 reg_addr; + u32 reg_value; +} MV_DRAM_MC_INIT; + +/* Board/Soc revisions define */ +enum board_rev { + Z1, + Z1_PCAC, + Z1_RD_SLED, + A0, + A0_AMC +}; + +typedef struct dram_modes { + char *mode_name; + u8 cpu_freq; + u8 fab_freq; + u8 chip_id; + int chip_board_rev; + MV_DRAM_MC_INIT *regs; + MV_DRAM_TRAINING_INIT *vals; +} MV_DRAM_MODES; + +/* + * Function Declarations + */ + +u32 cache_inv(u32 addr); +void flush_l1_v7(u32 line); +void flush_l1_v6(u32 line); + +u32 ddr3_cl_to_valid_cl(u32 cl); +u32 ddr3_valid_cl_to_cl(u32 ui_valid_cl); + +void ddr3_write_pup_reg(u32 mode, u32 cs, u32 pup, u32 phase, u32 delay); +u32 ddr3_read_pup_reg(u32 mode, u32 cs, u32 pup); + +int ddr3_sdram_pbs_compare(MV_DRAM_INFO *dram_info, u32 pup_locked, int is_tx, + u32 pbs_pattern_idx, u32 pbs_curr_val, + u32 pbs_lock_val, u32 *skew_array, + u8 *unlock_pup_dq_array, u32 ecc); + +int ddr3_sdram_dqs_compare(MV_DRAM_INFO *dram_info, u32 unlock_pup, + u32 *new_locked_pup, u32 *pattern, + u32 pattern_len, u32 sdram_offset, int write, + int mask, u32 *mask_pattern, int b_special_compare); + +int ddr3_sdram_compare(MV_DRAM_INFO *dram_info, u32 unlock_pup, + u32 *new_locked_pup, u32 *pattern, u32 pattern_len, + u32 sdram_offset, int write, int mask, + u32 *mask_pattern, int b_special_compare); + +int ddr3_sdram_direct_compare(MV_DRAM_INFO *dram_info, u32 unlock_pup, + u32 *new_locked_pup, u32 *pattern, + u32 pattern_len, u32 sdram_offset, int write, + int mask, u32 *mask_pattern); + +int ddr3_sdram_dm_compare(MV_DRAM_INFO *dram_info, u32 unlock_pup, + u32 *new_locked_pup, u32 *pattern, + u32 sdram_offset); +int ddr3_dram_sram_read(u32 src, u32 dst, u32 len); +int ddr3_load_patterns(MV_DRAM_INFO *dram_info, int resume); + +int ddr3_read_leveling_hw(u32 freq, MV_DRAM_INFO *dram_info); +int ddr3_read_leveling_sw(u32 freq, int ratio_2to1, MV_DRAM_INFO *dram_info); + +int ddr3_write_leveling_hw(u32 freq, MV_DRAM_INFO *dram_info); +int ddr3_write_leveling_sw(u32 freq, int ratio_2to1, MV_DRAM_INFO *dram_info); +int ddr3_write_leveling_hw_reg_dimm(u32 freq, MV_DRAM_INFO *dram_info); +int ddr3_wl_supplement(MV_DRAM_INFO *dram_info); + +int ddr3_dfs_high_2_low(u32 freq, MV_DRAM_INFO *dram_info); +int ddr3_dfs_low_2_high(u32 freq, int ratio_2to1, MV_DRAM_INFO *dram_info); + +int ddr3_pbs_tx(MV_DRAM_INFO *dram_info); +int ddr3_pbs_rx(MV_DRAM_INFO *dram_info); +int ddr3_load_pbs_patterns(MV_DRAM_INFO *dram_info); + +int ddr3_dqs_centralization_rx(MV_DRAM_INFO *dram_info); +int ddr3_dqs_centralization_tx(MV_DRAM_INFO *dram_info); +int ddr3_load_dqs_patterns(MV_DRAM_INFO *dram_info); + +void ddr3_static_training_init(void); + +u8 ddr3_get_eprom_fabric(void); +void ddr3_set_performance_params(MV_DRAM_INFO *dram_info); +int ddr3_dram_sram_burst(u32 src, u32 dst, u32 len); +void ddr3_save_training(MV_DRAM_INFO *dram_info); +int ddr3_read_training_results(void); +int ddr3_training_suspend_resume(MV_DRAM_INFO *dram_info); +int ddr3_get_min_max_read_sample_delay(u32 cs_enable, u32 reg, u32 *min, + u32 *max, u32 *cs_max); +int ddr3_get_min_max_rl_phase(MV_DRAM_INFO *dram_info, u32 *min, u32 *max, + u32 cs); +int ddr3_odt_activate(int activate); +int ddr3_odt_read_dynamic_config(MV_DRAM_INFO *dram_info); +void ddr3_print_freq(u32 freq); +void ddr3_reset_phy_read_fifo(void); + +#endif /* __DDR3_TRAINING_H */ diff --git a/drivers/ddr/mvebu/ddr3_init.c b/drivers/ddr/mvebu/ddr3_init.c new file mode 100644 index 0000000..11b8591 --- /dev/null +++ b/drivers/ddr/mvebu/ddr3_init.c @@ -0,0 +1,1219 @@ +/* + * Copyright (C) Marvell International Ltd. and its affiliates + * + * SPDX-License-Identifier: GPL-2.0 + */ + +#include <common.h> +#include <i2c.h> +#include <spl.h> +#include <asm/io.h> +#include <asm/arch/cpu.h> +#include <asm/arch/soc.h> + +#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 = DRAM_ECC; +#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 diff --git a/drivers/ddr/mvebu/ddr3_init.h b/drivers/ddr/mvebu/ddr3_init.h new file mode 100644 index 0000000..b259e09 --- /dev/null +++ b/drivers/ddr/mvebu/ddr3_init.h @@ -0,0 +1,143 @@ +/* + * Copyright (C) Marvell International Ltd. and its affiliates + * + * SPDX-License-Identifier: GPL-2.0 + */ + +#ifndef __DDR3_INIT_H +#define __DDR3_INIT_H + +/* + * Debug + */ + +/* + * MV_DEBUG_INIT need to be defines, otherwise the output of the + * DDR2 training code is not complete and misleading + */ +#define MV_DEBUG_INIT + +#ifdef MV_DEBUG_INIT +#define DEBUG_INIT_S(s) puts(s) +#define DEBUG_INIT_D(d, l) printf("%x", d) +#define DEBUG_INIT_D_10(d, l) printf("%d", d) +#else +#define DEBUG_INIT_S(s) +#define DEBUG_INIT_D(d, l) +#define DEBUG_INIT_D_10(d, l) +#endif + +#ifdef MV_DEBUG_INIT_FULL +#define DEBUG_INIT_FULL_S(s) puts(s) +#define DEBUG_INIT_FULL_D(d, l) printf("%x", d) +#define DEBUG_INIT_FULL_D_10(d, l) printf("%d", d) +#define DEBUG_WR_REG(reg, val) \ + { DEBUG_INIT_S("Write Reg: 0x"); DEBUG_INIT_D((reg), 8); \ + DEBUG_INIT_S("= "); DEBUG_INIT_D((val), 8); DEBUG_INIT_S("\n"); } +#define DEBUG_RD_REG(reg, val) \ + { DEBUG_INIT_S("Read Reg: 0x"); DEBUG_INIT_D((reg), 8); \ + DEBUG_INIT_S("= "); DEBUG_INIT_D((val), 8); DEBUG_INIT_S("\n"); } +#else +#define DEBUG_INIT_FULL_S(s) +#define DEBUG_INIT_FULL_D(d, l) +#define DEBUG_INIT_FULL_D_10(d, l) +#define DEBUG_WR_REG(reg, val) +#define DEBUG_RD_REG(reg, val) +#endif + +#define DEBUG_INIT_FULL_C(s, d, l) \ + { DEBUG_INIT_FULL_S(s); DEBUG_INIT_FULL_D(d, l); DEBUG_INIT_FULL_S("\n"); } +#define DEBUG_INIT_C(s, d, l) \ + { DEBUG_INIT_S(s); DEBUG_INIT_D(d, l); DEBUG_INIT_S("\n"); } + +#define MV_MBUS_REGS_OFFSET (0x20000) + +#include "ddr3_hw_training.h" + +#define MAX_DIMM_NUM 2 +#define SPD_SIZE 128 + +#ifdef MV88F78X60 +#include "ddr3_axp.h" +#elif defined(MV88F67XX) +#include "ddr3_a370.h" +#elif defined(MV88F672X) +#include "ddr3_a375.h" +#endif + +/* DRR training Error codes */ +/* Stage 0 errors */ +#define MV_DDR3_TRAINING_ERR_BAD_SAR 0xDD300001 +/* Stage 1 errors */ +#define MV_DDR3_TRAINING_ERR_TWSI_FAIL 0xDD301001 +#define MV_DDR3_TRAINING_ERR_DIMM_TYPE_NO_MATCH 0xDD301001 +#define MV_DDR3_TRAINING_ERR_TWSI_BAD_TYPE 0xDD301003 +#define MV_DDR3_TRAINING_ERR_BUS_WIDTH_NOT_MATCH 0xDD301004 +#define MV_DDR3_TRAINING_ERR_BAD_DIMM_SETUP 0xDD301005 +#define MV_DDR3_TRAINING_ERR_MAX_CS_LIMIT 0xDD301006 +#define MV_DDR3_TRAINING_ERR_MAX_ENA_CS_LIMIT 0xDD301007 +#define MV_DDR3_TRAINING_ERR_BAD_R_DIMM_SETUP 0xDD301008 +/* Stage 2 errors */ +#define MV_DDR3_TRAINING_ERR_HW_FAIL_BASE 0xDD302000 + +typedef enum config_type { + CONFIG_ECC, + CONFIG_MULTI_CS, + CONFIG_BUS_WIDTH +} MV_CONFIG_TYPE; + +enum log_level { + MV_LOG_LEVEL_0, + MV_LOG_LEVEL_1, + MV_LOG_LEVEL_2, + MV_LOG_LEVEL_3 +}; + +int ddr3_hw_training(u32 target_freq, u32 ddr_width, + int xor_bypass, u32 scrub_offs, u32 scrub_size, + int dqs_clk_aligned, int debug_mode, int reg_dimm_skip_wl); + +void ddr3_print_version(void); +void fix_pll_val(u8 target_fab); +u8 ddr3_get_eprom_fabric(void); +u32 ddr3_get_fab_opt(void); +u32 ddr3_get_cpu_freq(void); +u32 ddr3_get_vco_freq(void); +int ddr3_check_config(u32 addr, MV_CONFIG_TYPE config_type); +u32 ddr3_get_static_mc_value(u32 reg_addr, u32 offset1, u32 mask1, u32 offset2, + u32 mask2); +u32 ddr3_cl_to_valid_cl(u32 cl); +u32 ddr3_valid_cl_to_cl(u32 ui_valid_cl); +u32 ddr3_get_cs_num_from_reg(void); +u32 ddr3_get_cs_ena_from_reg(void); +u8 mv_ctrl_rev_get(void); + +u32 ddr3_get_log_level(void); + +/* SPD */ +int ddr3_dunit_setup(u32 ecc_ena, u32 hclk_time, u32 *ddr_width); + +/* + * Accessor functions for the registers + */ +static inline void reg_write(u32 addr, u32 val) +{ + writel(val, INTER_REGS_BASE + addr); +} + +static inline u32 reg_read(u32 addr) +{ + return readl(INTER_REGS_BASE + addr); +} + +static inline void reg_bit_set(u32 addr, u32 mask) +{ + setbits_le32(INTER_REGS_BASE + addr, mask); +} + +static inline void reg_bit_clr(u32 addr, u32 mask) +{ + clrbits_le32(INTER_REGS_BASE + addr, mask); +} + +#endif /* __DDR3_INIT_H */ diff --git a/drivers/ddr/mvebu/ddr3_patterns_64bit.h b/drivers/ddr/mvebu/ddr3_patterns_64bit.h new file mode 100644 index 0000000..1b57328 --- /dev/null +++ b/drivers/ddr/mvebu/ddr3_patterns_64bit.h @@ -0,0 +1,924 @@ +/* + * Copyright (C) Marvell International Ltd. and its affiliates + * + * SPDX-License-Identifier: GPL-2.0 + */ + +#ifndef __DDR3_PATTERNS_64_H +#define __DDR3_PATTERNS_64_H + +/* + * Patterns Declerations + */ + +u32 wl_sup_pattern[LEN_WL_SUP_PATTERN] __aligned(32) = { + 0x04030201, 0x08070605, 0x0c0b0a09, 0x100f0e0d, + 0x14131211, 0x18171615, 0x1c1b1a19, 0x201f1e1d, + 0x24232221, 0x28272625, 0x2c2b2a29, 0x302f2e2d, + 0x34333231, 0x38373635, 0x3c3b3a39, 0x403f3e3d, + 0x44434241, 0x48474645, 0x4c4b4a49, 0x504f4e4d, + 0x54535251, 0x58575655, 0x5c5b5a59, 0x605f5e5d, + 0x64636261, 0x68676665, 0x6c6b6a69, 0x706f6e6d, + 0x74737271, 0x78777675, 0x7c7b7a79, 0x807f7e7d +}; + +u32 pbs_pattern_32b[2][LEN_PBS_PATTERN] __aligned(32) = { + { + 0xAAAAAAAA, 0x55555555, 0xAAAAAAAA, 0x55555555, + 0xAAAAAAAA, 0x55555555, 0xAAAAAAAA, 0x55555555, + 0xAAAAAAAA, 0x55555555, 0xAAAAAAAA, 0x55555555, + 0xAAAAAAAA, 0x55555555, 0xAAAAAAAA, 0x55555555 + }, + { + 0x55555555, 0xAAAAAAAA, 0x55555555, 0xAAAAAAAA, + 0x55555555, 0xAAAAAAAA, 0x55555555, 0xAAAAAAAA, + 0x55555555, 0xAAAAAAAA, 0x55555555, 0xAAAAAAAA, + 0x55555555, 0xAAAAAAAA, 0x55555555, 0xAAAAAAAA + } +}; + +u32 pbs_pattern_64b[2][LEN_PBS_PATTERN] __aligned(32) = { + { + 0xAAAAAAAA, 0xAAAAAAAA, 0x55555555, 0x55555555, + 0xAAAAAAAA, 0xAAAAAAAA, 0x55555555, 0x55555555, + 0xAAAAAAAA, 0xAAAAAAAA, 0x55555555, 0x55555555, + 0xAAAAAAAA, 0xAAAAAAAA, 0x55555555, 0x55555555 + }, + { + 0x55555555, 0x55555555, 0xAAAAAAAA, 0xAAAAAAAA, + 0x55555555, 0x55555555, 0xAAAAAAAA, 0xAAAAAAAA, + 0x55555555, 0x55555555, 0xAAAAAAAA, 0xAAAAAAAA, + 0x55555555, 0x55555555, 0xAAAAAAAA, 0xAAAAAAAA + } +}; + +u32 rl_pattern[LEN_STD_PATTERN] __aligned(32) = { + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x01010101, 0x01010101, 0x01010101, 0x01010101 +}; + +u32 killer_pattern_32b[DQ_NUM][LEN_KILLER_PATTERN] __aligned(32) = { + { + 0x01010101, 0x00000000, 0x01010101, 0xFFFFFFFF, + 0x01010101, 0x00000000, 0x01010101, 0xFFFFFFFF, + 0xFEFEFEFE, 0xFEFEFEFE, 0x01010101, 0xFEFEFEFE, + 0xFEFEFEFE, 0xFEFEFEFE, 0x01010101, 0xFEFEFEFE, + 0x01010101, 0xFEFEFEFE, 0x01010101, 0x01010101, + 0x01010101, 0xFEFEFEFE, 0x01010101, 0x01010101, + 0xFEFEFEFE, 0x01010101, 0xFEFEFEFE, 0x00000000, + 0xFEFEFEFE, 0x01010101, 0xFEFEFEFE, 0x00000000, + 0xFFFFFFFF, 0x00000000, 0xFFFFFFFF, 0x00000000, + 0xFFFFFFFF, 0x00000000, 0xFFFFFFFF, 0x00000000, + 0xFFFFFFFF, 0x00000000, 0xFFFFFFFF, 0x01010101, + 0xFFFFFFFF, 0x00000000, 0xFFFFFFFF, 0x01010101, + 0x00000000, 0xFFFFFFFF, 0x00000000, 0xFFFFFFFF, + 0x00000000, 0xFFFFFFFF, 0x00000000, 0xFFFFFFFF, + 0x00000000, 0x00000000, 0x00000000, 0xFEFEFEFE, + 0x00000000, 0x00000000, 0x00000000, 0xFEFEFEFE, + 0xFEFEFEFE, 0xFFFFFFFF, 0x00000000, 0x00000000, + 0xFEFEFEFE, 0xFFFFFFFF, 0x00000000, 0x00000000, + 0xFFFFFFFF, 0xFFFFFFFF, 0x00000000, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0x00000000, 0xFFFFFFFF, + 0x00000000, 0xFFFFFFFF, 0xFFFFFFFF, 0x00000000, + 0x00000000, 0xFFFFFFFF, 0xFFFFFFFF, 0x00000000, + 0x00000000, 0xFFFFFFFF, 0x00000000, 0xFFFFFFFF, + 0x00000000, 0xFFFFFFFF, 0x00000000, 0xFFFFFFFF, + 0xFEFEFEFE, 0x00000000, 0xFEFEFEFE, 0x00000000, + 0xFEFEFEFE, 0x00000000, 0xFEFEFEFE, 0x00000000, + 0x00000000, 0xFFFFFFFF, 0xFFFFFFFF, 0x01010101, + 0x00000000, 0xFFFFFFFF, 0xFFFFFFFF, 0x01010101, + 0xFFFFFFFF, 0xFFFFFFFF, 0x01010101, 0x00000000, + 0xFFFFFFFF, 0xFFFFFFFF, 0x01010101, 0x00000000, + 0x01010101, 0xFFFFFFFF, 0xFEFEFEFE, 0xFEFEFEFE, + 0x01010101, 0xFFFFFFFF, 0xFEFEFEFE, 0xFEFEFEFE + }, + { + 0x02020202, 0x00000000, 0x02020202, 0xFFFFFFFF, + 0x02020202, 0x00000000, 0x02020202, 0xFFFFFFFF, + 0xFDFDFDFD, 0xFDFDFDFD, 0x02020202, 0xFDFDFDFD, + 0xFDFDFDFD, 0xFDFDFDFD, 0x02020202, 0xFDFDFDFD, + 0x02020202, 0xFDFDFDFD, 0x02020202, 0x02020202, + 0x02020202, 0xFDFDFDFD, 0x02020202, 0x02020202, + 0xFDFDFDFD, 0x02020202, 0xFDFDFDFD, 0x00000000, + 0xFDFDFDFD, 0x02020202, 0xFDFDFDFD, 0x00000000, + 0xFFFFFFFF, 0x00000000, 0xFFFFFFFF, 0x00000000, + 0xFFFFFFFF, 0x00000000, 0xFFFFFFFF, 0x00000000, + 0xFFFFFFFF, 0x00000000, 0xFFFFFFFF, 0x02020202, + 0xFFFFFFFF, 0x00000000, 0xFFFFFFFF, 0x02020202, + 0x00000000, 0xFFFFFFFF, 0x00000000, 0xFFFFFFFF, + 0x00000000, 0xFFFFFFFF, 0x00000000, 0xFFFFFFFF, + 0x00000000, 0x00000000, 0x00000000, 0xFDFDFDFD, + 0x00000000, 0x00000000, 0x00000000, 0xFDFDFDFD, + 0xFDFDFDFD, 0xFFFFFFFF, 0x00000000, 0x00000000, + 0xFDFDFDFD, 0xFFFFFFFF, 0x00000000, 0x00000000, + 0xFFFFFFFF, 0xFFFFFFFF, 0x00000000, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0x00000000, 0xFFFFFFFF, + 0x00000000, 0xFFFFFFFF, 0xFFFFFFFF, 0x00000000, + 0x00000000, 0xFFFFFFFF, 0xFFFFFFFF, 0x00000000, + 0x00000000, 0xFFFFFFFF, 0x00000000, 0xFFFFFFFF, + 0x00000000, 0xFFFFFFFF, 0x00000000, 0xFFFFFFFF, + 0xFDFDFDFD, 0x00000000, 0xFDFDFDFD, 0x00000000, + 0xFDFDFDFD, 0x00000000, 0xFDFDFDFD, 0x00000000, + 0x00000000, 0xFFFFFFFF, 0xFFFFFFFF, 0x02020202, + 0x00000000, 0xFFFFFFFF, 0xFFFFFFFF, 0x02020202, + 0xFFFFFFFF, 0xFFFFFFFF, 0x02020202, 0x00000000, + 0xFFFFFFFF, 0xFFFFFFFF, 0x02020202, 0x00000000, + 0x02020202, 0xFFFFFFFF, 0xFDFDFDFD, 0xFDFDFDFD, + 0x02020202, 0xFFFFFFFF, 0xFDFDFDFD, 0xFDFDFDFD + }, + { + 0x04040404, 0x00000000, 0x04040404, 0xFFFFFFFF, + 0x04040404, 0x00000000, 0x04040404, 0xFFFFFFFF, + 0xFBFBFBFB, 0xFBFBFBFB, 0x04040404, 0xFBFBFBFB, + 0xFBFBFBFB, 0xFBFBFBFB, 0x04040404, 0xFBFBFBFB, + 0x04040404, 0xFBFBFBFB, 0x04040404, 0x04040404, + 0x04040404, 0xFBFBFBFB, 0x04040404, 0x04040404, + 0xFBFBFBFB, 0x04040404, 0xFBFBFBFB, 0x00000000, + 0xFBFBFBFB, 0x04040404, 0xFBFBFBFB, 0x00000000, + 0xFFFFFFFF, 0x00000000, 0xFFFFFFFF, 0x00000000, + 0xFFFFFFFF, 0x00000000, 0xFFFFFFFF, 0x00000000, + 0xFFFFFFFF, 0x00000000, 0xFFFFFFFF, 0x04040404, + 0xFFFFFFFF, 0x00000000, 0xFFFFFFFF, 0x04040404, + 0x00000000, 0xFFFFFFFF, 0x00000000, 0xFFFFFFFF, + 0x00000000, 0xFFFFFFFF, 0x00000000, 0xFFFFFFFF, + 0x00000000, 0x00000000, 0x00000000, 0xFBFBFBFB, + 0x00000000, 0x00000000, 0x00000000, 0xFBFBFBFB, + 0xFBFBFBFB, 0xFFFFFFFF, 0x00000000, 0x00000000, + 0xFBFBFBFB, 0xFFFFFFFF, 0x00000000, 0x00000000, + 0xFFFFFFFF, 0xFFFFFFFF, 0x00000000, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0x00000000, 0xFFFFFFFF, + 0x00000000, 0xFFFFFFFF, 0xFFFFFFFF, 0x00000000, + 0x00000000, 0xFFFFFFFF, 0xFFFFFFFF, 0x00000000, + 0x00000000, 0xFFFFFFFF, 0x00000000, 0xFFFFFFFF, + 0x00000000, 0xFFFFFFFF, 0x00000000, 0xFFFFFFFF, + 0xFBFBFBFB, 0x00000000, 0xFBFBFBFB, 0x00000000, + 0xFBFBFBFB, 0x00000000, 0xFBFBFBFB, 0x00000000, + 0x00000000, 0xFFFFFFFF, 0xFFFFFFFF, 0x04040404, + 0x00000000, 0xFFFFFFFF, 0xFFFFFFFF, 0x04040404, + 0xFFFFFFFF, 0xFFFFFFFF, 0x04040404, 0x00000000, + 0xFFFFFFFF, 0xFFFFFFFF, 0x04040404, 0x00000000, + 0x04040404, 0xFFFFFFFF, 0xFBFBFBFB, 0xFBFBFBFB, + 0x04040404, 0xFFFFFFFF, 0xFBFBFBFB, 0xFBFBFBFB + }, + { + 0x08080808, 0x00000000, 0x08080808, 0xFFFFFFFF, + 0x08080808, 0x00000000, 0x08080808, 0xFFFFFFFF, + 0xF7F7F7F7, 0xF7F7F7F7, 0x08080808, 0xF7F7F7F7, + 0xF7F7F7F7, 0xF7F7F7F7, 0x08080808, 0xF7F7F7F7, + 0x08080808, 0xF7F7F7F7, 0x08080808, 0x08080808, + 0x08080808, 0xF7F7F7F7, 0x08080808, 0x08080808, + 0xF7F7F7F7, 0x08080808, 0xF7F7F7F7, 0x00000000, + 0xF7F7F7F7, 0x08080808, 0xF7F7F7F7, 0x00000000, + 0xFFFFFFFF, 0x00000000, 0xFFFFFFFF, 0x00000000, + 0xFFFFFFFF, 0x00000000, 0xFFFFFFFF, 0x00000000, + 0xFFFFFFFF, 0x00000000, 0xFFFFFFFF, 0x08080808, + 0xFFFFFFFF, 0x00000000, 0xFFFFFFFF, 0x08080808, + 0x00000000, 0xFFFFFFFF, 0x00000000, 0xFFFFFFFF, + 0x00000000, 0xFFFFFFFF, 0x00000000, 0xFFFFFFFF, + 0x00000000, 0x00000000, 0x00000000, 0xF7F7F7F7, + 0x00000000, 0x00000000, 0x00000000, 0xF7F7F7F7, + 0xF7F7F7F7, 0xFFFFFFFF, 0x00000000, 0x00000000, + 0xF7F7F7F7, 0xFFFFFFFF, 0x00000000, 0x00000000, + 0xFFFFFFFF, 0xFFFFFFFF, 0x00000000, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0x00000000, 0xFFFFFFFF, + 0x00000000, 0xFFFFFFFF, 0xFFFFFFFF, 0x00000000, + 0x00000000, 0xFFFFFFFF, 0xFFFFFFFF, 0x00000000, + 0x00000000, 0xFFFFFFFF, 0x00000000, 0xFFFFFFFF, + 0x00000000, 0xFFFFFFFF, 0x00000000, 0xFFFFFFFF, + 0xF7F7F7F7, 0x00000000, 0xF7F7F7F7, 0x00000000, + 0xF7F7F7F7, 0x00000000, 0xF7F7F7F7, 0x00000000, + 0x00000000, 0xFFFFFFFF, 0xFFFFFFFF, 0x08080808, + 0x00000000, 0xFFFFFFFF, 0xFFFFFFFF, 0x08080808, + 0xFFFFFFFF, 0xFFFFFFFF, 0x08080808, 0x00000000, + 0xFFFFFFFF, 0xFFFFFFFF, 0x08080808, 0x00000000, + 0x08080808, 0xFFFFFFFF, 0xF7F7F7F7, 0xF7F7F7F7, + 0x08080808, 0xFFFFFFFF, 0xF7F7F7F7, 0xF7F7F7F7 + }, + { + 0x10101010, 0x00000000, 0x10101010, 0xFFFFFFFF, + 0x10101010, 0x00000000, 0x10101010, 0xFFFFFFFF, + 0xEFEFEFEF, 0xEFEFEFEF, 0x10101010, 0xEFEFEFEF, + 0xEFEFEFEF, 0xEFEFEFEF, 0x10101010, 0xEFEFEFEF, + 0x10101010, 0xEFEFEFEF, 0x10101010, 0x10101010, + 0x10101010, 0xEFEFEFEF, 0x10101010, 0x10101010, + 0xEFEFEFEF, 0x10101010, 0xEFEFEFEF, 0x00000000, + 0xEFEFEFEF, 0x10101010, 0xEFEFEFEF, 0x00000000, + 0xFFFFFFFF, 0x00000000, 0xFFFFFFFF, 0x00000000, + 0xFFFFFFFF, 0x00000000, 0xFFFFFFFF, 0x00000000, + 0xFFFFFFFF, 0x00000000, 0xFFFFFFFF, 0x10101010, + 0xFFFFFFFF, 0x00000000, 0xFFFFFFFF, 0x10101010, + 0x00000000, 0xFFFFFFFF, 0x00000000, 0xFFFFFFFF, + 0x00000000, 0xFFFFFFFF, 0x00000000, 0xFFFFFFFF, + 0x00000000, 0x00000000, 0x00000000, 0xEFEFEFEF, + 0x00000000, 0x00000000, 0x00000000, 0xEFEFEFEF, + 0xEFEFEFEF, 0xFFFFFFFF, 0x00000000, 0x00000000, + 0xEFEFEFEF, 0xFFFFFFFF, 0x00000000, 0x00000000, + 0xFFFFFFFF, 0xFFFFFFFF, 0x00000000, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0x00000000, 0xFFFFFFFF, + 0x00000000, 0xFFFFFFFF, 0xFFFFFFFF, 0x00000000, + 0x00000000, 0xFFFFFFFF, 0xFFFFFFFF, 0x00000000, + 0x00000000, 0xFFFFFFFF, 0x00000000, 0xFFFFFFFF, + 0x00000000, 0xFFFFFFFF, 0x00000000, 0xFFFFFFFF, + 0xEFEFEFEF, 0x00000000, 0xEFEFEFEF, 0x00000000, + 0xEFEFEFEF, 0x00000000, 0xEFEFEFEF, 0x00000000, + 0x00000000, 0xFFFFFFFF, 0xFFFFFFFF, 0x10101010, + 0x00000000, 0xFFFFFFFF, 0xFFFFFFFF, 0x10101010, + 0xFFFFFFFF, 0xFFFFFFFF, 0x10101010, 0x00000000, + 0xFFFFFFFF, 0xFFFFFFFF, 0x10101010, 0x00000000, + 0x10101010, 0xFFFFFFFF, 0xEFEFEFEF, 0xEFEFEFEF, + 0x10101010, 0xFFFFFFFF, 0xEFEFEFEF, 0xEFEFEFEF + }, + { + 0x20202020, 0x00000000, 0x20202020, 0xFFFFFFFF, + 0x20202020, 0x00000000, 0x20202020, 0xFFFFFFFF, + 0xDFDFDFDF, 0xDFDFDFDF, 0x20202020, 0xDFDFDFDF, + 0xDFDFDFDF, 0xDFDFDFDF, 0x20202020, 0xDFDFDFDF, + 0x20202020, 0xDFDFDFDF, 0x20202020, 0x20202020, + 0x20202020, 0xDFDFDFDF, 0x20202020, 0x20202020, + 0xDFDFDFDF, 0x20202020, 0xDFDFDFDF, 0x00000000, + 0xDFDFDFDF, 0x20202020, 0xDFDFDFDF, 0x00000000, + 0xFFFFFFFF, 0x00000000, 0xFFFFFFFF, 0x00000000, + 0xFFFFFFFF, 0x00000000, 0xFFFFFFFF, 0x00000000, + 0xFFFFFFFF, 0x00000000, 0xFFFFFFFF, 0x20202020, + 0xFFFFFFFF, 0x00000000, 0xFFFFFFFF, 0x20202020, + 0x00000000, 0xFFFFFFFF, 0x00000000, 0xFFFFFFFF, + 0x00000000, 0xFFFFFFFF, 0x00000000, 0xFFFFFFFF, + 0x00000000, 0x00000000, 0x00000000, 0xDFDFDFDF, + 0x00000000, 0x00000000, 0x00000000, 0xDFDFDFDF, + 0xDFDFDFDF, 0xFFFFFFFF, 0x00000000, 0x00000000, + 0xDFDFDFDF, 0xFFFFFFFF, 0x00000000, 0x00000000, + 0xFFFFFFFF, 0xFFFFFFFF, 0x00000000, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0x00000000, 0xFFFFFFFF, + 0x00000000, 0xFFFFFFFF, 0xFFFFFFFF, 0x00000000, + 0x00000000, 0xFFFFFFFF, 0xFFFFFFFF, 0x00000000, + 0x00000000, 0xFFFFFFFF, 0x00000000, 0xFFFFFFFF, + 0x00000000, 0xFFFFFFFF, 0x00000000, 0xFFFFFFFF, + 0xDFDFDFDF, 0x00000000, 0xDFDFDFDF, 0x00000000, + 0xDFDFDFDF, 0x00000000, 0xDFDFDFDF, 0x00000000, + 0x00000000, 0xFFFFFFFF, 0xFFFFFFFF, 0x20202020, + 0x00000000, 0xFFFFFFFF, 0xFFFFFFFF, 0x20202020, + 0xFFFFFFFF, 0xFFFFFFFF, 0x20202020, 0x00000000, + 0xFFFFFFFF, 0xFFFFFFFF, 0x20202020, 0x00000000, + 0x20202020, 0xFFFFFFFF, 0xDFDFDFDF, 0xDFDFDFDF, + 0x20202020, 0xFFFFFFFF, 0xDFDFDFDF, 0xDFDFDFDF + }, + { + 0x40404040, 0x00000000, 0x40404040, 0xFFFFFFFF, + 0x40404040, 0x00000000, 0x40404040, 0xFFFFFFFF, + 0xBFBFBFBF, 0xBFBFBFBF, 0x40404040, 0xBFBFBFBF, + 0xBFBFBFBF, 0xBFBFBFBF, 0x40404040, 0xBFBFBFBF, + 0x40404040, 0xBFBFBFBF, 0x40404040, 0x40404040, + 0x40404040, 0xBFBFBFBF, 0x40404040, 0x40404040, + 0xBFBFBFBF, 0x40404040, 0xBFBFBFBF, 0x00000000, + 0xBFBFBFBF, 0x40404040, 0xBFBFBFBF, 0x00000000, + 0xFFFFFFFF, 0x00000000, 0xFFFFFFFF, 0x00000000, + 0xFFFFFFFF, 0x00000000, 0xFFFFFFFF, 0x00000000, + 0xFFFFFFFF, 0x00000000, 0xFFFFFFFF, 0x40404040, + 0xFFFFFFFF, 0x00000000, 0xFFFFFFFF, 0x40404040, + 0x00000000, 0xFFFFFFFF, 0x00000000, 0xFFFFFFFF, + 0x00000000, 0xFFFFFFFF, 0x00000000, 0xFFFFFFFF, + 0x00000000, 0x00000000, 0x00000000, 0xBFBFBFBF, + 0x00000000, 0x00000000, 0x00000000, 0xBFBFBFBF, + 0xBFBFBFBF, 0xFFFFFFFF, 0x00000000, 0x00000000, + 0xBFBFBFBF, 0xFFFFFFFF, 0x00000000, 0x00000000, + 0xFFFFFFFF, 0xFFFFFFFF, 0x00000000, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0x00000000, 0xFFFFFFFF, + 0x00000000, 0xFFFFFFFF, 0xFFFFFFFF, 0x00000000, + 0x00000000, 0xFFFFFFFF, 0xFFFFFFFF, 0x00000000, + 0x00000000, 0xFFFFFFFF, 0x00000000, 0xFFFFFFFF, + 0x00000000, 0xFFFFFFFF, 0x00000000, 0xFFFFFFFF, + 0xBFBFBFBF, 0x00000000, 0xBFBFBFBF, 0x00000000, + 0xBFBFBFBF, 0x00000000, 0xBFBFBFBF, 0x00000000, + 0x00000000, 0xFFFFFFFF, 0xFFFFFFFF, 0x40404040, + 0x00000000, 0xFFFFFFFF, 0xFFFFFFFF, 0x40404040, + 0xFFFFFFFF, 0xFFFFFFFF, 0x40404040, 0x00000000, + 0xFFFFFFFF, 0xFFFFFFFF, 0x40404040, 0x00000000, + 0x40404040, 0xFFFFFFFF, 0xBFBFBFBF, 0xBFBFBFBF, + 0x40404040, 0xFFFFFFFF, 0xBFBFBFBF, 0xBFBFBFBF + }, + { + 0x80808080, 0x00000000, 0x80808080, 0xFFFFFFFF, + 0x80808080, 0x00000000, 0x80808080, 0xFFFFFFFF, + 0x7F7F7F7F, 0x7F7F7F7F, 0x80808080, 0x7F7F7F7F, + 0x7F7F7F7F, 0x7F7F7F7F, 0x80808080, 0x7F7F7F7F, + 0x80808080, 0x7F7F7F7F, 0x80808080, 0x80808080, + 0x80808080, 0x7F7F7F7F, 0x80808080, 0x80808080, + 0x7F7F7F7F, 0x80808080, 0x7F7F7F7F, 0x00000000, + 0x7F7F7F7F, 0x80808080, 0x7F7F7F7F, 0x00000000, + 0xFFFFFFFF, 0x00000000, 0xFFFFFFFF, 0x00000000, + 0xFFFFFFFF, 0x00000000, 0xFFFFFFFF, 0x00000000, + 0xFFFFFFFF, 0x00000000, 0xFFFFFFFF, 0x80808080, + 0xFFFFFFFF, 0x00000000, 0xFFFFFFFF, 0x80808080, + 0x00000000, 0xFFFFFFFF, 0x00000000, 0xFFFFFFFF, + 0x00000000, 0xFFFFFFFF, 0x00000000, 0xFFFFFFFF, + 0x00000000, 0x00000000, 0x00000000, 0x7F7F7F7F, + 0x00000000, 0x00000000, 0x00000000, 0x7F7F7F7F, + 0x7F7F7F7F, 0xFFFFFFFF, 0x00000000, 0x00000000, + 0x7F7F7F7F, 0xFFFFFFFF, 0x00000000, 0x00000000, + 0xFFFFFFFF, 0xFFFFFFFF, 0x00000000, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0x00000000, 0xFFFFFFFF, + 0x00000000, 0xFFFFFFFF, 0xFFFFFFFF, 0x00000000, + 0x00000000, 0xFFFFFFFF, 0xFFFFFFFF, 0x00000000, + 0x00000000, 0xFFFFFFFF, 0x00000000, 0xFFFFFFFF, + 0x00000000, 0xFFFFFFFF, 0x00000000, 0xFFFFFFFF, + 0x7F7F7F7F, 0x00000000, 0x7F7F7F7F, 0x00000000, + 0x7F7F7F7F, 0x00000000, 0x7F7F7F7F, 0x00000000, + 0x00000000, 0xFFFFFFFF, 0xFFFFFFFF, 0x80808080, + 0x00000000, 0xFFFFFFFF, 0xFFFFFFFF, 0x80808080, + 0xFFFFFFFF, 0xFFFFFFFF, 0x80808080, 0x00000000, + 0xFFFFFFFF, 0xFFFFFFFF, 0x80808080, 0x00000000, + 0x80808080, 0xFFFFFFFF, 0x7F7F7F7F, 0x7F7F7F7F, + 0x80808080, 0xFFFFFFFF, 0x7F7F7F7F, 0x7F7F7F7F + } +}; + +u32 killer_pattern_64b[DQ_NUM][LEN_KILLER_PATTERN] __aligned(32) = { + { + 0x01010101, 0x01010101, 0x00000000, 0x00000000, + 0x01010101, 0x01010101, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFEFEFEFE, 0xFEFEFEFE, 0xFEFEFEFE, 0xFEFEFEFE, + 0x01010101, 0x01010101, 0xFEFEFEFE, 0xFEFEFEFE, + 0x01010101, 0x01010101, 0xFEFEFEFE, 0xFEFEFEFE, + 0x01010101, 0x01010101, 0x01010101, 0x01010101, + 0xFEFEFEFE, 0xFEFEFEFE, 0x01010101, 0x01010101, + 0xFEFEFEFE, 0xFEFEFEFE, 0x00000000, 0x00000000, + 0xFFFFFFFF, 0xFFFFFFFF, 0x00000000, 0x00000000, + 0xFFFFFFFF, 0xFFFFFFFF, 0x00000000, 0x00000000, + 0xFFFFFFFF, 0xFFFFFFFF, 0x00000000, 0x00000000, + 0xFFFFFFFF, 0xFFFFFFFF, 0x01010101, 0x01010101, + 0x00000000, 0x00000000, 0xFFFFFFFF, 0xFFFFFFFF, + 0x00000000, 0x00000000, 0xFFFFFFFF, 0xFFFFFFFF, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0xFEFEFEFE, 0xFEFEFEFE, + 0xFEFEFEFE, 0xFEFEFEFE, 0xFFFFFFFF, 0xFFFFFFFF, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0x00000000, 0x00000000, 0xFFFFFFFF, 0xFFFFFFFF, + 0x00000000, 0x00000000, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0xFFFFFFFF, 0xFFFFFFFF, + 0x00000000, 0x00000000, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFEFEFEFE, 0xFEFEFEFE, 0x00000000, 0x00000000, + 0xFEFEFEFE, 0xFEFEFEFE, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0x01010101, 0x01010101, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0x01010101, 0x01010101, 0x00000000, 0x00000000, + 0x01010101, 0x01010101, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFEFEFEFE, 0xFEFEFEFE, 0xFEFEFEFE, 0xFEFEFEFE + }, + { + 0x02020202, 0x02020202, 0x00000000, 0x00000000, + 0x02020202, 0x02020202, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFDFDFDFD, 0xFDFDFDFD, 0xFDFDFDFD, 0xFDFDFDFD, + 0x02020202, 0x02020202, 0xFDFDFDFD, 0xFDFDFDFD, + 0x02020202, 0x02020202, 0xFDFDFDFD, 0xFDFDFDFD, + 0x02020202, 0x02020202, 0x02020202, 0x02020202, + 0xFDFDFDFD, 0xFDFDFDFD, 0x02020202, 0x02020202, + 0xFDFDFDFD, 0xFDFDFDFD, 0x00000000, 0x00000000, + 0xFFFFFFFF, 0xFFFFFFFF, 0x00000000, 0x00000000, + 0xFFFFFFFF, 0xFFFFFFFF, 0x00000000, 0x00000000, + 0xFFFFFFFF, 0xFFFFFFFF, 0x00000000, 0x00000000, + 0xFFFFFFFF, 0xFFFFFFFF, 0x02020202, 0x02020202, + 0x00000000, 0x00000000, 0xFFFFFFFF, 0xFFFFFFFF, + 0x00000000, 0x00000000, 0xFFFFFFFF, 0xFFFFFFFF, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0xFDFDFDFD, 0xFDFDFDFD, + 0xFDFDFDFD, 0xFDFDFDFD, 0xFFFFFFFF, 0xFFFFFFFF, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0x00000000, 0x00000000, 0xFFFFFFFF, 0xFFFFFFFF, + 0x00000000, 0x00000000, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0xFFFFFFFF, 0xFFFFFFFF, + 0x00000000, 0x00000000, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFDFDFDFD, 0xFDFDFDFD, 0x00000000, 0x00000000, + 0xFDFDFDFD, 0xFDFDFDFD, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0x02020202, 0x02020202, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0x02020202, 0x02020202, 0x00000000, 0x00000000, + 0x02020202, 0x02020202, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFDFDFDFD, 0xFDFDFDFD, 0xFDFDFDFD, 0xFDFDFDFD + }, + { + 0x04040404, 0x04040404, 0x00000000, 0x00000000, + 0x04040404, 0x04040404, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFBFBFBFB, 0xFBFBFBFB, 0xFBFBFBFB, 0xFBFBFBFB, + 0x04040404, 0x04040404, 0xFBFBFBFB, 0xFBFBFBFB, + 0x04040404, 0x04040404, 0xFBFBFBFB, 0xFBFBFBFB, + 0x04040404, 0x04040404, 0x04040404, 0x04040404, + 0xFBFBFBFB, 0xFBFBFBFB, 0x04040404, 0x04040404, + 0xFBFBFBFB, 0xFBFBFBFB, 0x00000000, 0x00000000, + 0xFFFFFFFF, 0xFFFFFFFF, 0x00000000, 0x00000000, + 0xFFFFFFFF, 0xFFFFFFFF, 0x00000000, 0x00000000, + 0xFFFFFFFF, 0xFFFFFFFF, 0x00000000, 0x00000000, + 0xFFFFFFFF, 0xFFFFFFFF, 0x04040404, 0x04040404, + 0x00000000, 0x00000000, 0xFFFFFFFF, 0xFFFFFFFF, + 0x00000000, 0x00000000, 0xFFFFFFFF, 0xFFFFFFFF, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0xFBFBFBFB, 0xFBFBFBFB, + 0xFBFBFBFB, 0xFBFBFBFB, 0xFFFFFFFF, 0xFFFFFFFF, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0x00000000, 0x00000000, 0xFFFFFFFF, 0xFFFFFFFF, + 0x00000000, 0x00000000, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0xFFFFFFFF, 0xFFFFFFFF, + 0x00000000, 0x00000000, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFBFBFBFB, 0xFBFBFBFB, 0x00000000, 0x00000000, + 0xFBFBFBFB, 0xFBFBFBFB, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0x04040404, 0x04040404, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0x04040404, 0x04040404, 0x00000000, 0x00000000, + 0x04040404, 0x04040404, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFBFBFBFB, 0xFBFBFBFB, 0xFBFBFBFB, 0xFBFBFBFB + }, + { + 0x08080808, 0x08080808, 0x00000000, 0x00000000, + 0x08080808, 0x08080808, 0xFFFFFFFF, 0xFFFFFFFF, + 0xF7F7F7F7, 0xF7F7F7F7, 0xF7F7F7F7, 0xF7F7F7F7, + 0x08080808, 0x08080808, 0xF7F7F7F7, 0xF7F7F7F7, + 0x08080808, 0x08080808, 0xF7F7F7F7, 0xF7F7F7F7, + 0x08080808, 0x08080808, 0x08080808, 0x08080808, + 0xF7F7F7F7, 0xF7F7F7F7, 0x08080808, 0x08080808, + 0xF7F7F7F7, 0xF7F7F7F7, 0x00000000, 0x00000000, + 0xFFFFFFFF, 0xFFFFFFFF, 0x00000000, 0x00000000, + 0xFFFFFFFF, 0xFFFFFFFF, 0x00000000, 0x00000000, + 0xFFFFFFFF, 0xFFFFFFFF, 0x00000000, 0x00000000, + 0xFFFFFFFF, 0xFFFFFFFF, 0x08080808, 0x08080808, + 0x00000000, 0x00000000, 0xFFFFFFFF, 0xFFFFFFFF, + 0x00000000, 0x00000000, 0xFFFFFFFF, 0xFFFFFFFF, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0xF7F7F7F7, 0xF7F7F7F7, + 0xF7F7F7F7, 0xF7F7F7F7, 0xFFFFFFFF, 0xFFFFFFFF, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0x00000000, 0x00000000, 0xFFFFFFFF, 0xFFFFFFFF, + 0x00000000, 0x00000000, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0xFFFFFFFF, 0xFFFFFFFF, + 0x00000000, 0x00000000, 0xFFFFFFFF, 0xFFFFFFFF, + 0xF7F7F7F7, 0xF7F7F7F7, 0x00000000, 0x00000000, + 0xF7F7F7F7, 0xF7F7F7F7, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0x08080808, 0x08080808, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0x08080808, 0x08080808, 0x00000000, 0x00000000, + 0x08080808, 0x08080808, 0xFFFFFFFF, 0xFFFFFFFF, + 0xF7F7F7F7, 0xF7F7F7F7, 0xF7F7F7F7, 0xF7F7F7F7 + }, + { + 0x10101010, 0x10101010, 0x00000000, 0x00000000, + 0x10101010, 0x10101010, 0xFFFFFFFF, 0xFFFFFFFF, + 0xEFEFEFEF, 0xEFEFEFEF, 0xEFEFEFEF, 0xEFEFEFEF, + 0x10101010, 0x10101010, 0xEFEFEFEF, 0xEFEFEFEF, + 0x10101010, 0x10101010, 0xEFEFEFEF, 0xEFEFEFEF, + 0x10101010, 0x10101010, 0x10101010, 0x10101010, + 0xEFEFEFEF, 0xEFEFEFEF, 0x10101010, 0x10101010, + 0xEFEFEFEF, 0xEFEFEFEF, 0x00000000, 0x00000000, + 0xFFFFFFFF, 0xFFFFFFFF, 0x00000000, 0x00000000, + 0xFFFFFFFF, 0xFFFFFFFF, 0x00000000, 0x00000000, + 0xFFFFFFFF, 0xFFFFFFFF, 0x00000000, 0x00000000, + 0xFFFFFFFF, 0xFFFFFFFF, 0x10101010, 0x10101010, + 0x00000000, 0x00000000, 0xFFFFFFFF, 0xFFFFFFFF, + 0x00000000, 0x00000000, 0xFFFFFFFF, 0xFFFFFFFF, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0xEFEFEFEF, 0xEFEFEFEF, + 0xEFEFEFEF, 0xEFEFEFEF, 0xFFFFFFFF, 0xFFFFFFFF, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0x00000000, 0x00000000, 0xFFFFFFFF, 0xFFFFFFFF, + 0x00000000, 0x00000000, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0xFFFFFFFF, 0xFFFFFFFF, + 0x00000000, 0x00000000, 0xFFFFFFFF, 0xFFFFFFFF, + 0xEFEFEFEF, 0xEFEFEFEF, 0x00000000, 0x00000000, + 0xEFEFEFEF, 0xEFEFEFEF, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0x10101010, 0x10101010, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0x10101010, 0x10101010, 0x00000000, 0x00000000, + 0x10101010, 0x10101010, 0xFFFFFFFF, 0xFFFFFFFF, + 0xEFEFEFEF, 0xEFEFEFEF, 0xEFEFEFEF, 0xEFEFEFEF + }, + { + 0x20202020, 0x20202020, 0x00000000, 0x00000000, + 0x20202020, 0x20202020, 0xFFFFFFFF, 0xFFFFFFFF, + 0xDFDFDFDF, 0xDFDFDFDF, 0xDFDFDFDF, 0xDFDFDFDF, + 0x20202020, 0x20202020, 0xDFDFDFDF, 0xDFDFDFDF, + 0x20202020, 0x20202020, 0xDFDFDFDF, 0xDFDFDFDF, + 0x20202020, 0x20202020, 0x20202020, 0x20202020, + 0xDFDFDFDF, 0xDFDFDFDF, 0x20202020, 0x20202020, + 0xDFDFDFDF, 0xDFDFDFDF, 0x00000000, 0x00000000, + 0xFFFFFFFF, 0xFFFFFFFF, 0x00000000, 0x00000000, + 0xFFFFFFFF, 0xFFFFFFFF, 0x00000000, 0x00000000, + 0xFFFFFFFF, 0xFFFFFFFF, 0x00000000, 0x00000000, + 0xFFFFFFFF, 0xFFFFFFFF, 0x20202020, 0x20202020, + 0x00000000, 0x00000000, 0xFFFFFFFF, 0xFFFFFFFF, + 0x00000000, 0x00000000, 0xFFFFFFFF, 0xFFFFFFFF, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0xDFDFDFDF, 0xDFDFDFDF, + 0xDFDFDFDF, 0xDFDFDFDF, 0xFFFFFFFF, 0xFFFFFFFF, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0x00000000, 0x00000000, 0xFFFFFFFF, 0xFFFFFFFF, + 0x00000000, 0x00000000, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0xFFFFFFFF, 0xFFFFFFFF, + 0x00000000, 0x00000000, 0xFFFFFFFF, 0xFFFFFFFF, + 0xDFDFDFDF, 0xDFDFDFDF, 0x00000000, 0x00000000, + 0xDFDFDFDF, 0xDFDFDFDF, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0x20202020, 0x20202020, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0x20202020, 0x20202020, 0x00000000, 0x00000000, + 0x20202020, 0x20202020, 0xFFFFFFFF, 0xFFFFFFFF, + 0xDFDFDFDF, 0xDFDFDFDF, 0xDFDFDFDF, 0xDFDFDFDF + }, + { + 0x40404040, 0x40404040, 0x00000000, 0x00000000, + 0x40404040, 0x40404040, 0xFFFFFFFF, 0xFFFFFFFF, + 0xBFBFBFBF, 0xBFBFBFBF, 0xBFBFBFBF, 0xBFBFBFBF, + 0x40404040, 0x40404040, 0xBFBFBFBF, 0xBFBFBFBF, + 0x40404040, 0x40404040, 0xBFBFBFBF, 0xBFBFBFBF, + 0x40404040, 0x40404040, 0x40404040, 0x40404040, + 0xBFBFBFBF, 0xBFBFBFBF, 0x40404040, 0x40404040, + 0xBFBFBFBF, 0xBFBFBFBF, 0x00000000, 0x00000000, + 0xFFFFFFFF, 0xFFFFFFFF, 0x00000000, 0x00000000, + 0xFFFFFFFF, 0xFFFFFFFF, 0x00000000, 0x00000000, + 0xFFFFFFFF, 0xFFFFFFFF, 0x00000000, 0x00000000, + 0xFFFFFFFF, 0xFFFFFFFF, 0x40404040, 0x40404040, + 0x00000000, 0x00000000, 0xFFFFFFFF, 0xFFFFFFFF, + 0x00000000, 0x00000000, 0xFFFFFFFF, 0xFFFFFFFF, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0xBFBFBFBF, 0xBFBFBFBF, + 0xBFBFBFBF, 0xBFBFBFBF, 0xFFFFFFFF, 0xFFFFFFFF, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0x00000000, 0x00000000, 0xFFFFFFFF, 0xFFFFFFFF, + 0x00000000, 0x00000000, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0xFFFFFFFF, 0xFFFFFFFF, + 0x00000000, 0x00000000, 0xFFFFFFFF, 0xFFFFFFFF, + 0xBFBFBFBF, 0xBFBFBFBF, 0x00000000, 0x00000000, + 0xBFBFBFBF, 0xBFBFBFBF, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0x40404040, 0x40404040, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0x40404040, 0x40404040, 0x00000000, 0x00000000, + 0x40404040, 0x40404040, 0xFFFFFFFF, 0xFFFFFFFF, + 0xBFBFBFBF, 0xBFBFBFBF, 0xBFBFBFBF, 0xBFBFBFBF + }, + { + 0x80808080, 0x80808080, 0x00000000, 0x00000000, + 0x80808080, 0x80808080, 0xFFFFFFFF, 0xFFFFFFFF, + 0x7F7F7F7F, 0x7F7F7F7F, 0x7F7F7F7F, 0x7F7F7F7F, + 0x80808080, 0x80808080, 0x7F7F7F7F, 0x7F7F7F7F, + 0x80808080, 0x80808080, 0x7F7F7F7F, 0x7F7F7F7F, + 0x80808080, 0x80808080, 0x80808080, 0x80808080, + 0x7F7F7F7F, 0x7F7F7F7F, 0x80808080, 0x80808080, + 0x7F7F7F7F, 0x7F7F7F7F, 0x00000000, 0x00000000, + 0xFFFFFFFF, 0xFFFFFFFF, 0x00000000, 0x00000000, + 0xFFFFFFFF, 0xFFFFFFFF, 0x00000000, 0x00000000, + 0xFFFFFFFF, 0xFFFFFFFF, 0x00000000, 0x00000000, + 0xFFFFFFFF, 0xFFFFFFFF, 0x80808080, 0x80808080, + 0x00000000, 0x00000000, 0xFFFFFFFF, 0xFFFFFFFF, + 0x00000000, 0x00000000, 0xFFFFFFFF, 0xFFFFFFFF, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x7F7F7F7F, 0x7F7F7F7F, + 0x7F7F7F7F, 0x7F7F7F7F, 0xFFFFFFFF, 0xFFFFFFFF, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0x00000000, 0x00000000, 0xFFFFFFFF, 0xFFFFFFFF, + 0x00000000, 0x00000000, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0xFFFFFFFF, 0xFFFFFFFF, + 0x00000000, 0x00000000, 0xFFFFFFFF, 0xFFFFFFFF, + 0x7F7F7F7F, 0x7F7F7F7F, 0x00000000, 0x00000000, + 0x7F7F7F7F, 0x7F7F7F7F, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0x80808080, 0x80808080, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0x80808080, 0x80808080, 0x00000000, 0x00000000, + 0x80808080, 0x80808080, 0xFFFFFFFF, 0xFFFFFFFF, + 0x7F7F7F7F, 0x7F7F7F7F, 0x7F7F7F7F, 0x7F7F7F7F + } +}; + +u32 special_pattern[DQ_NUM][LEN_SPECIAL_PATTERN] __aligned(32) = { + { + 0x00000000, 0x00000000, 0x01010101, 0x01010101, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFEFEFEFE, 0xFEFEFEFE, + 0xFEFEFEFE, 0xFEFEFEFE, 0x01010101, 0x01010101, + 0xFEFEFEFE, 0xFEFEFEFE, 0x01010101, 0x01010101, + 0xFEFEFEFE, 0xFEFEFEFE, 0x01010101, 0x01010101, + 0x01010101, 0x01010101, 0xFEFEFEFE, 0xFEFEFEFE, + 0x01010101, 0x01010101, 0xFEFEFEFE, 0xFEFEFEFE, + 0x00000000, 0x00000000, 0xFFFFFFFF, 0xFFFFFFFF, + 0x00000000, 0x00000000, 0xFFFFFFFF, 0xFFFFFFFF, + 0x00000000, 0x00000000, 0xFFFFFFFF, 0xFFFFFFFF, + 0x00000000, 0x00000000, 0xFFFFFFFF, 0xFFFFFFFF, + 0x01010101, 0x01010101, 0x00000000, 0x00000000, + 0xFFFFFFFF, 0xFFFFFFFF, 0x00000000, 0x00000000, + 0xFFFFFFFF, 0xFFFFFFFF, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0xFEFEFEFE, 0xFEFEFEFE, 0xFEFEFEFE, 0xFEFEFEFE, + 0xFFFFFFFF, 0xFFFFFFFF, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0x00000000, 0x00000000, + 0xFFFFFFFF, 0xFFFFFFFF, 0x00000000, 0x00000000, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0xFFFFFFFF, 0xFFFFFFFF, 0x00000000, 0x00000000, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFEFEFEFE, 0xFEFEFEFE, + 0x00000000, 0x00000000, 0xFEFEFEFE, 0xFEFEFEFE, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0x01010101, 0x01010101, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0x01010101, 0x01010101, + 0x00000000, 0x00000000, 0x01010101, 0x01010101, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFEFEFEFE, 0xFEFEFEFE, + 0xFEFEFEFE, 0xFEFEFEFE, 0x00000000, 0x00000000 + }, + { + 0x00000000, 0x00000000, 0x02020202, 0x02020202, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFDFDFDFD, 0xFDFDFDFD, + 0xFDFDFDFD, 0xFDFDFDFD, 0x02020202, 0x02020202, + 0xFDFDFDFD, 0xFDFDFDFD, 0x02020202, 0x02020202, + 0xFDFDFDFD, 0xFDFDFDFD, 0x02020202, 0x02020202, + 0x02020202, 0x02020202, 0xFDFDFDFD, 0xFDFDFDFD, + 0x02020202, 0x02020202, 0xFDFDFDFD, 0xFDFDFDFD, + 0x00000000, 0x00000000, 0xFFFFFFFF, 0xFFFFFFFF, + 0x00000000, 0x00000000, 0xFFFFFFFF, 0xFFFFFFFF, + 0x00000000, 0x00000000, 0xFFFFFFFF, 0xFFFFFFFF, + 0x00000000, 0x00000000, 0xFFFFFFFF, 0xFFFFFFFF, + 0x02020202, 0x02020202, 0x00000000, 0x00000000, + 0xFFFFFFFF, 0xFFFFFFFF, 0x00000000, 0x00000000, + 0xFFFFFFFF, 0xFFFFFFFF, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0xFDFDFDFD, 0xFDFDFDFD, 0xFDFDFDFD, 0xFDFDFDFD, + 0xFFFFFFFF, 0xFFFFFFFF, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0x00000000, 0x00000000, + 0xFFFFFFFF, 0xFFFFFFFF, 0x00000000, 0x00000000, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0xFFFFFFFF, 0xFFFFFFFF, 0x00000000, 0x00000000, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFDFDFDFD, 0xFDFDFDFD, + 0x00000000, 0x00000000, 0xFDFDFDFD, 0xFDFDFDFD, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0x02020202, 0x02020202, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0x02020202, 0x02020202, + 0x00000000, 0x00000000, 0x02020202, 0x02020202, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFDFDFDFD, 0xFDFDFDFD, + 0xFDFDFDFD, 0xFDFDFDFD, 0x00000000, 0x00000000 + }, + { + 0x00000000, 0x00000000, 0x04040404, 0x04040404, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFBFBFBFB, 0xFBFBFBFB, + 0xFBFBFBFB, 0xFBFBFBFB, 0x04040404, 0x04040404, + 0xFBFBFBFB, 0xFBFBFBFB, 0x04040404, 0x04040404, + 0xFBFBFBFB, 0xFBFBFBFB, 0x04040404, 0x04040404, + 0x04040404, 0x04040404, 0xFBFBFBFB, 0xFBFBFBFB, + 0x04040404, 0x04040404, 0xFBFBFBFB, 0xFBFBFBFB, + 0x00000000, 0x00000000, 0xFFFFFFFF, 0xFFFFFFFF, + 0x00000000, 0x00000000, 0xFFFFFFFF, 0xFFFFFFFF, + 0x00000000, 0x00000000, 0xFFFFFFFF, 0xFFFFFFFF, + 0x00000000, 0x00000000, 0xFFFFFFFF, 0xFFFFFFFF, + 0x04040404, 0x04040404, 0x00000000, 0x00000000, + 0xFFFFFFFF, 0xFFFFFFFF, 0x00000000, 0x00000000, + 0xFFFFFFFF, 0xFFFFFFFF, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0xFBFBFBFB, 0xFBFBFBFB, 0xFBFBFBFB, 0xFBFBFBFB, + 0xFFFFFFFF, 0xFFFFFFFF, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0x00000000, 0x00000000, + 0xFFFFFFFF, 0xFFFFFFFF, 0x00000000, 0x00000000, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0xFFFFFFFF, 0xFFFFFFFF, 0x00000000, 0x00000000, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFBFBFBFB, 0xFBFBFBFB, + 0x00000000, 0x00000000, 0xFBFBFBFB, 0xFBFBFBFB, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0x04040404, 0x04040404, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0x04040404, 0x04040404, + 0x00000000, 0x00000000, 0x04040404, 0x04040404, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFBFBFBFB, 0xFBFBFBFB, + 0xFBFBFBFB, 0xFBFBFBFB, 0x00000000, 0x00000000 + }, + { + 0x00000000, 0x00000000, 0x08080808, 0x08080808, + 0xFFFFFFFF, 0xFFFFFFFF, 0xF7F7F7F7, 0xF7F7F7F7, + 0xF7F7F7F7, 0xF7F7F7F7, 0x08080808, 0x08080808, + 0xF7F7F7F7, 0xF7F7F7F7, 0x08080808, 0x08080808, + 0xF7F7F7F7, 0xF7F7F7F7, 0x08080808, 0x08080808, + 0x08080808, 0x08080808, 0xF7F7F7F7, 0xF7F7F7F7, + 0x08080808, 0x08080808, 0xF7F7F7F7, 0xF7F7F7F7, + 0x00000000, 0x00000000, 0xFFFFFFFF, 0xFFFFFFFF, + 0x00000000, 0x00000000, 0xFFFFFFFF, 0xFFFFFFFF, + 0x00000000, 0x00000000, 0xFFFFFFFF, 0xFFFFFFFF, + 0x00000000, 0x00000000, 0xFFFFFFFF, 0xFFFFFFFF, + 0x08080808, 0x08080808, 0x00000000, 0x00000000, + 0xFFFFFFFF, 0xFFFFFFFF, 0x00000000, 0x00000000, + 0xFFFFFFFF, 0xFFFFFFFF, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0xF7F7F7F7, 0xF7F7F7F7, 0xF7F7F7F7, 0xF7F7F7F7, + 0xFFFFFFFF, 0xFFFFFFFF, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0x00000000, 0x00000000, + 0xFFFFFFFF, 0xFFFFFFFF, 0x00000000, 0x00000000, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0xFFFFFFFF, 0xFFFFFFFF, 0x00000000, 0x00000000, + 0xFFFFFFFF, 0xFFFFFFFF, 0xF7F7F7F7, 0xF7F7F7F7, + 0x00000000, 0x00000000, 0xF7F7F7F7, 0xF7F7F7F7, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0x08080808, 0x08080808, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0x08080808, 0x08080808, + 0x00000000, 0x00000000, 0x08080808, 0x08080808, + 0xFFFFFFFF, 0xFFFFFFFF, 0xF7F7F7F7, 0xF7F7F7F7, + 0xF7F7F7F7, 0xF7F7F7F7, 0x00000000, 0x00000000 + }, + { + 0x00000000, 0x00000000, 0x10101010, 0x10101010, + 0xFFFFFFFF, 0xFFFFFFFF, 0xEFEFEFEF, 0xEFEFEFEF, + 0xEFEFEFEF, 0xEFEFEFEF, 0x10101010, 0x10101010, + 0xEFEFEFEF, 0xEFEFEFEF, 0x10101010, 0x10101010, + 0xEFEFEFEF, 0xEFEFEFEF, 0x10101010, 0x10101010, + 0x10101010, 0x10101010, 0xEFEFEFEF, 0xEFEFEFEF, + 0x10101010, 0x10101010, 0xEFEFEFEF, 0xEFEFEFEF, + 0x00000000, 0x00000000, 0xFFFFFFFF, 0xFFFFFFFF, + 0x00000000, 0x00000000, 0xFFFFFFFF, 0xFFFFFFFF, + 0x00000000, 0x00000000, 0xFFFFFFFF, 0xFFFFFFFF, + 0x00000000, 0x00000000, 0xFFFFFFFF, 0xFFFFFFFF, + 0x10101010, 0x10101010, 0x00000000, 0x00000000, + 0xFFFFFFFF, 0xFFFFFFFF, 0x00000000, 0x00000000, + 0xFFFFFFFF, 0xFFFFFFFF, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0xEFEFEFEF, 0xEFEFEFEF, 0xEFEFEFEF, 0xEFEFEFEF, + 0xFFFFFFFF, 0xFFFFFFFF, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0x00000000, 0x00000000, + 0xFFFFFFFF, 0xFFFFFFFF, 0x00000000, 0x00000000, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0xFFFFFFFF, 0xFFFFFFFF, 0x00000000, 0x00000000, + 0xFFFFFFFF, 0xFFFFFFFF, 0xEFEFEFEF, 0xEFEFEFEF, + 0x00000000, 0x00000000, 0xEFEFEFEF, 0xEFEFEFEF, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0x10101010, 0x10101010, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0x10101010, 0x10101010, + 0x00000000, 0x00000000, 0x10101010, 0x10101010, + 0xFFFFFFFF, 0xFFFFFFFF, 0xEFEFEFEF, 0xEFEFEFEF, + 0xEFEFEFEF, 0xEFEFEFEF, 0x00000000, 0x00000000 + }, + { + 0x00000000, 0x00000000, 0x20202020, 0x20202020, + 0xFFFFFFFF, 0xFFFFFFFF, 0xDFDFDFDF, 0xDFDFDFDF, + 0xDFDFDFDF, 0xDFDFDFDF, 0x20202020, 0x20202020, + 0xDFDFDFDF, 0xDFDFDFDF, 0x20202020, 0x20202020, + 0xDFDFDFDF, 0xDFDFDFDF, 0x20202020, 0x20202020, + 0x20202020, 0x20202020, 0xDFDFDFDF, 0xDFDFDFDF, + 0x20202020, 0x20202020, 0xDFDFDFDF, 0xDFDFDFDF, + 0x00000000, 0x00000000, 0xFFFFFFFF, 0xFFFFFFFF, + 0x00000000, 0x00000000, 0xFFFFFFFF, 0xFFFFFFFF, + 0x00000000, 0x00000000, 0xFFFFFFFF, 0xFFFFFFFF, + 0x00000000, 0x00000000, 0xFFFFFFFF, 0xFFFFFFFF, + 0x20202020, 0x20202020, 0x00000000, 0x00000000, + 0xFFFFFFFF, 0xFFFFFFFF, 0x00000000, 0x00000000, + 0xFFFFFFFF, 0xFFFFFFFF, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0xDFDFDFDF, 0xDFDFDFDF, 0xDFDFDFDF, 0xDFDFDFDF, + 0xFFFFFFFF, 0xFFFFFFFF, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0x00000000, 0x00000000, + 0xFFFFFFFF, 0xFFFFFFFF, 0x00000000, 0x00000000, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0xFFFFFFFF, 0xFFFFFFFF, 0x00000000, 0x00000000, + 0xFFFFFFFF, 0xFFFFFFFF, 0xDFDFDFDF, 0xDFDFDFDF, + 0x00000000, 0x00000000, 0xDFDFDFDF, 0xDFDFDFDF, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0x20202020, 0x20202020, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0x20202020, 0x20202020, + 0x00000000, 0x00000000, 0x20202020, 0x20202020, + 0xFFFFFFFF, 0xFFFFFFFF, 0xDFDFDFDF, 0xDFDFDFDF, + 0xDFDFDFDF, 0xDFDFDFDF, 0x00000000, 0x00000000 + }, + { + 0x00000000, 0x00000000, 0x40404040, 0x40404040, + 0xFFFFFFFF, 0xFFFFFFFF, 0xBFBFBFBF, 0xBFBFBFBF, + 0xBFBFBFBF, 0xBFBFBFBF, 0x40404040, 0x40404040, + 0xBFBFBFBF, 0xBFBFBFBF, 0x40404040, 0x40404040, + 0xBFBFBFBF, 0xBFBFBFBF, 0x40404040, 0x40404040, + 0x40404040, 0x40404040, 0xBFBFBFBF, 0xBFBFBFBF, + 0x40404040, 0x40404040, 0xBFBFBFBF, 0xBFBFBFBF, + 0x00000000, 0x00000000, 0xFFFFFFFF, 0xFFFFFFFF, + 0x00000000, 0x00000000, 0xFFFFFFFF, 0xFFFFFFFF, + 0x00000000, 0x00000000, 0xFFFFFFFF, 0xFFFFFFFF, + 0x00000000, 0x00000000, 0xFFFFFFFF, 0xFFFFFFFF, + 0x40404040, 0x40404040, 0x00000000, 0x00000000, + 0xFFFFFFFF, 0xFFFFFFFF, 0x00000000, 0x00000000, + 0xFFFFFFFF, 0xFFFFFFFF, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0xBFBFBFBF, 0xBFBFBFBF, 0xBFBFBFBF, 0xBFBFBFBF, + 0xFFFFFFFF, 0xFFFFFFFF, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0x00000000, 0x00000000, + 0xFFFFFFFF, 0xFFFFFFFF, 0x00000000, 0x00000000, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0xFFFFFFFF, 0xFFFFFFFF, 0x00000000, 0x00000000, + 0xFFFFFFFF, 0xFFFFFFFF, 0xBFBFBFBF, 0xBFBFBFBF, + 0x00000000, 0x00000000, 0xBFBFBFBF, 0xBFBFBFBF, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0x40404040, 0x40404040, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0x40404040, 0x40404040, + 0x00000000, 0x00000000, 0x40404040, 0x40404040, + 0xFFFFFFFF, 0xFFFFFFFF, 0xBFBFBFBF, 0xBFBFBFBF, + 0xBFBFBFBF, 0xBFBFBFBF, 0x00000000, 0x00000000 + }, + { + 0x00000000, 0x00000000, 0x80808080, 0x80808080, + 0xFFFFFFFF, 0xFFFFFFFF, 0x7F7F7F7F, 0x7F7F7F7F, + 0x7F7F7F7F, 0x7F7F7F7F, 0x80808080, 0x80808080, + 0x7F7F7F7F, 0x7F7F7F7F, 0x80808080, 0x80808080, + 0x7F7F7F7F, 0x7F7F7F7F, 0x80808080, 0x80808080, + 0x80808080, 0x80808080, 0x7F7F7F7F, 0x7F7F7F7F, + 0x80808080, 0x80808080, 0x7F7F7F7F, 0x7F7F7F7F, + 0x00000000, 0x00000000, 0xFFFFFFFF, 0xFFFFFFFF, + 0x00000000, 0x00000000, 0xFFFFFFFF, 0xFFFFFFFF, + 0x00000000, 0x00000000, 0xFFFFFFFF, 0xFFFFFFFF, + 0x00000000, 0x00000000, 0xFFFFFFFF, 0xFFFFFFFF, + 0x80808080, 0x80808080, 0x00000000, 0x00000000, + 0xFFFFFFFF, 0xFFFFFFFF, 0x00000000, 0x00000000, + 0xFFFFFFFF, 0xFFFFFFFF, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x7F7F7F7F, 0x7F7F7F7F, 0x7F7F7F7F, 0x7F7F7F7F, + 0xFFFFFFFF, 0xFFFFFFFF, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0x00000000, 0x00000000, + 0xFFFFFFFF, 0xFFFFFFFF, 0x00000000, 0x00000000, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0xFFFFFFFF, 0xFFFFFFFF, 0x00000000, 0x00000000, + 0xFFFFFFFF, 0xFFFFFFFF, 0x7F7F7F7F, 0x7F7F7F7F, + 0x00000000, 0x00000000, 0x7F7F7F7F, 0x7F7F7F7F, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, + 0x80808080, 0x80808080, 0xFFFFFFFF, 0xFFFFFFFF, + 0xFFFFFFFF, 0xFFFFFFFF, 0x80808080, 0x80808080, + 0x00000000, 0x00000000, 0x80808080, 0x80808080, + 0xFFFFFFFF, 0xFFFFFFFF, 0x7F7F7F7F, 0x7F7F7F7F, + 0x7F7F7F7F, 0x7F7F7F7F, 0x00000000, 0x00000000 + } +}; + +/* Fabric ratios table */ +u32 fabric_ratio[FAB_OPT] = { + 0x04010204, + 0x04020202, + 0x08020306, + 0x08020303, + 0x04020303, + 0x04020204, + 0x04010202, + 0x08030606, + 0x08030505, + 0x04020306, + 0x0804050A, + 0x04030606, + 0x04020404, + 0x04030306, + 0x04020505, + 0x08020505, + 0x04010303, + 0x08050A0A, + 0x04030408, + 0x04010102, + 0x08030306 +}; + +u32 pbs_dq_mapping[PUP_NUM_64BIT + 1][DQ_NUM] = { + {3, 2, 5, 7, 1, 0, 6, 4}, + {2, 3, 6, 7, 1, 0, 4, 5}, + {1, 3, 5, 6, 0, 2, 4, 7}, + {0, 2, 4, 7, 1, 3, 5, 6}, + {3, 0, 4, 6, 1, 2, 5, 7}, + {0, 3, 5, 7, 1, 2, 4, 6}, + {2, 3, 5, 7, 1, 0, 4, 6}, + {0, 2, 5, 4, 1, 3, 6, 7}, + {2, 3, 4, 7, 0, 1, 5, 6} +}; + +#endif /* __DDR3_PATTERNS_64_H */ diff --git a/drivers/ddr/mvebu/ddr3_pbs.c b/drivers/ddr/mvebu/ddr3_pbs.c new file mode 100644 index 0000000..00ea3fd --- /dev/null +++ b/drivers/ddr/mvebu/ddr3_pbs.c @@ -0,0 +1,1592 @@ +/* + * Copyright (C) Marvell International Ltd. and its affiliates + * + * SPDX-License-Identifier: GPL-2.0 + */ + +#include <common.h> +#include <i2c.h> +#include <spl.h> +#include <asm/io.h> +#include <asm/arch/cpu.h> +#include <asm/arch/soc.h> + +#include "ddr3_hw_training.h" + +/* + * Debug + */ +#define DEBUG_PBS_FULL_C(s, d, l) \ + DEBUG_PBS_FULL_S(s); DEBUG_PBS_FULL_D(d, l); DEBUG_PBS_FULL_S("\n") +#define DEBUG_PBS_C(s, d, l) \ + DEBUG_PBS_S(s); DEBUG_PBS_D(d, l); DEBUG_PBS_S("\n") + +#ifdef MV_DEBUG_PBS +#define DEBUG_PBS_S(s) puts(s) +#define DEBUG_PBS_D(d, l) printf("%x", d) +#else +#define DEBUG_PBS_S(s) +#define DEBUG_PBS_D(d, l) +#endif + +#ifdef MV_DEBUG_FULL_PBS +#define DEBUG_PBS_FULL_S(s) puts(s) +#define DEBUG_PBS_FULL_D(d, l) printf("%x", d) +#else +#define DEBUG_PBS_FULL_S(s) +#define DEBUG_PBS_FULL_D(d, l) +#endif + +#if defined(MV88F78X60) || defined(MV88F672X) + +/* Temp array for skew data storage */ +static u32 skew_array[(MAX_PUP_NUM) * DQ_NUM] = { 0 }; + +/* PBS locked dq (per pup) */ +extern u32 pbs_locked_dq[MAX_PUP_NUM][DQ_NUM]; +extern u32 pbs_locked_dm[MAX_PUP_NUM]; +extern u32 pbs_locked_value[MAX_PUP_NUM][DQ_NUM]; + +#if defined(MV88F672X) +extern u32 pbs_pattern[2][LEN_16BIT_PBS_PATTERN]; +extern u32 pbs_pattern_32b[2][LEN_PBS_PATTERN]; +#else +extern u32 pbs_pattern_32b[2][LEN_PBS_PATTERN]; +extern u32 pbs_pattern_64b[2][LEN_PBS_PATTERN]; +#endif + +extern u32 pbs_dq_mapping[PUP_NUM_64BIT + 1][DQ_NUM]; + +static int ddr3_tx_shift_dqs_adll_step_before_fail(MV_DRAM_INFO *dram_info, + u32 cur_pup, u32 pbs_pattern_idx, u32 ecc); +static int ddr3_rx_shift_dqs_to_first_fail(MV_DRAM_INFO *dram_info, u32 cur_pup, + u32 pbs_pattern_idx, u32 ecc); +static int ddr3_pbs_per_bit(MV_DRAM_INFO *dram_info, int *start_over, int is_tx, + u32 *pcur_pup, u32 pbs_pattern_idx, u32 ecc); +static int ddr3_set_pbs_results(MV_DRAM_INFO *dram_info, int is_tx); +static void ddr3_pbs_write_pup_dqs_reg(u32 cs, u32 pup, u32 dqs_delay); + +/* + * Name: ddr3_pbs_tx + * Desc: Execute the PBS TX phase. + * Args: dram_info ddr3 training information struct + * Notes: + * Returns: MV_OK if success, other error code if fail. + */ +int ddr3_pbs_tx(MV_DRAM_INFO *dram_info) +{ + /* Array of Deskew results */ + + /* + * Array to hold the total sum of skew from all iterations + * (for average purpose) + */ + u32 skew_sum_array[MAX_PUP_NUM][DQ_NUM] = { {0} }; + + /* + * Array to hold the total average skew from both patterns + * (for average purpose) + */ + u32 pattern_skew_array[MAX_PUP_NUM][DQ_NUM] = { {0} }; + + u32 pbs_rep_time = 0; /* counts number of loop in case of fail */ + /* bit array for unlock pups - used to repeat on the RX operation */ + u32 cur_pup; + u32 max_pup; + u32 pbs_retry; + u32 pup, dq, pups, cur_max_pup, valid_pup, reg; + u32 pattern_idx; + u32 ecc; + /* indicates whether we need to start the loop again */ + int start_over; + + DEBUG_PBS_S("DDR3 - PBS TX - Starting PBS TX procedure\n"); + + pups = dram_info->num_of_total_pups; + max_pup = dram_info->num_of_total_pups; + + /* Enable SW override */ + reg = reg_read(REG_DRAM_TRAINING_2_ADDR) | + (1 << REG_DRAM_TRAINING_2_SW_OVRD_OFFS); + /* [0] = 1 - Enable SW override */ + /* 0x15B8 - Training SW 2 Register */ + reg_write(REG_DRAM_TRAINING_2_ADDR, reg); + DEBUG_PBS_S("DDR3 - PBS RX - SW Override Enabled\n"); + + reg = 1 << REG_DRAM_TRAINING_AUTO_OFFS; + reg_write(REG_DRAM_TRAINING_ADDR, reg); /* 0x15B0 - Training Register */ + + /* Running twice for 2 different patterns. each patterns - 3 times */ + for (pattern_idx = 0; pattern_idx < COUNT_PBS_PATTERN; pattern_idx++) { + DEBUG_PBS_C("DDR3 - PBS TX - Working with pattern - ", + pattern_idx, 1); + + /* Reset sum array */ + for (pup = 0; pup < pups; pup++) { + for (dq = 0; dq < DQ_NUM; dq++) + skew_sum_array[pup][dq] = 0; + } + + /* + * Perform PBS several of times (3 for each pattern). + * At the end, we'll use the average + */ + /* If there is ECC, do each PBS again with mux change */ + for (pbs_retry = 0; pbs_retry < COUNT_PBS_REPEAT; pbs_retry++) { + for (ecc = 0; ecc < (dram_info->ecc_ena + 1); ecc++) { + + /* + * This parameter stores the current PUP + * num - ecc mode dependent - 4-8 / 1 pups + */ + cur_max_pup = (1 - ecc) * + dram_info->num_of_std_pups + ecc; + + if (ecc) { + /* Only 1 pup in this case */ + valid_pup = 0x1; + } else if (cur_max_pup > 4) { + /* 64 bit - 8 pups */ + valid_pup = 0xFF; + } else if (cur_max_pup == 4) { + /* 32 bit - 4 pups */ + valid_pup = 0xF; + } else { + /* 16 bit - 2 pups */ + valid_pup = 0x3; + } + + /* ECC Support - Switch ECC Mux on ecc=1 */ + reg = reg_read(REG_DRAM_TRAINING_2_ADDR) & + ~(1 << REG_DRAM_TRAINING_2_ECC_MUX_OFFS); + reg |= (dram_info->ecc_ena * ecc << + REG_DRAM_TRAINING_2_ECC_MUX_OFFS); + reg_write(REG_DRAM_TRAINING_2_ADDR, reg); + + if (ecc) + DEBUG_PBS_S("DDR3 - PBS Tx - ECC Mux Enabled\n"); + else + DEBUG_PBS_S("DDR3 - PBS Tx - ECC Mux Disabled\n"); + + /* Init iteration values */ + /* Clear the locked DQs */ + for (pup = 0; pup < cur_max_pup; pup++) { + for (dq = 0; dq < DQ_NUM; dq++) { + pbs_locked_dq[ + pup + ecc * + (max_pup - 1)][dq] = + 0; + } + } + + pbs_rep_time = 0; + cur_pup = valid_pup; + start_over = 0; + + /* + * Run loop On current Pattern and current + * pattern iteration (just to cover the false + * fail problem) + */ + do { + DEBUG_PBS_S("DDR3 - PBS Tx - Pbs Rep Loop is "); + DEBUG_PBS_D(pbs_rep_time, 1); + DEBUG_PBS_S(", for Retry No."); + DEBUG_PBS_D(pbs_retry, 1); + DEBUG_PBS_S("\n"); + + /* Set all PBS values to MIN (0) */ + DEBUG_PBS_S("DDR3 - PBS Tx - Set all PBS values to MIN\n"); + + for (dq = 0; dq < DQ_NUM; dq++) { + ddr3_write_pup_reg( + PUP_PBS_TX + + pbs_dq_mapping[pup * + (1 - ecc) + + ecc * ECC_PUP] + [dq], CS0, (1 - ecc) * + PUP_BC + ecc * ECC_PUP, 0, + 0); + } + + /* + * Shift DQ ADLL right, One step before + * fail + */ + DEBUG_PBS_S("DDR3 - PBS Tx - ADLL shift right one phase before fail\n"); + + if (MV_OK != ddr3_tx_shift_dqs_adll_step_before_fail + (dram_info, cur_pup, pattern_idx, + ecc)) + return MV_DDR3_TRAINING_ERR_PBS_ADLL_SHR_1PHASE; + + /* PBS For each bit */ + DEBUG_PBS_S("DDR3 - PBS Tx - perform PBS for each bit\n"); + + /* + * In this stage - start_over = 0 + */ + if (MV_OK != ddr3_pbs_per_bit( + dram_info, &start_over, 1, + &cur_pup, pattern_idx, ecc)) + return MV_DDR3_TRAINING_ERR_PBS_TX_PER_BIT; + + } while ((start_over == 1) && + (++pbs_rep_time < COUNT_PBS_STARTOVER)); + + if (pbs_rep_time == COUNT_PBS_STARTOVER && + start_over == 1) { + DEBUG_PBS_S("DDR3 - PBS Tx - FAIL - Adll reach max value\n"); + return MV_DDR3_TRAINING_ERR_PBS_TX_MAX_VAL; + } + + DEBUG_PBS_FULL_C("DDR3 - PBS TX - values for iteration - ", + pbs_retry, 1); + for (pup = 0; pup < cur_max_pup; pup++) { + /* + * To minimize delay elements, inc + * from pbs value the min pbs val + */ + DEBUG_PBS_S("DDR3 - PBS - PUP"); + DEBUG_PBS_D((pup + (ecc * ECC_PUP)), 1); + DEBUG_PBS_S(": "); + + for (dq = 0; dq < DQ_NUM; dq++) { + /* Set skew value for all dq */ + /* + * Bit# Deskew <- Bit# Deskew - + * last / first failing bit + * Deskew For all bits (per PUP) + * (minimize delay elements) + */ + DEBUG_PBS_S("DQ"); + DEBUG_PBS_D(dq, 1); + DEBUG_PBS_S("-"); + DEBUG_PBS_D(skew_array + [((pup) * DQ_NUM) + + dq], 2); + DEBUG_PBS_S(", "); + } + DEBUG_PBS_S("\n"); + } + + /* + * Collect the results we got on this trial + * of PBS + */ + for (pup = 0; pup < cur_max_pup; pup++) { + for (dq = 0; dq < DQ_NUM; dq++) { + skew_sum_array[pup + (ecc * (max_pup - 1))] + [dq] += skew_array + [((pup) * DQ_NUM) + dq]; + } + } + + /* ECC Support - Disable ECC MUX */ + reg = reg_read(REG_DRAM_TRAINING_2_ADDR) & + ~(1 << REG_DRAM_TRAINING_2_ECC_MUX_OFFS); + reg_write(REG_DRAM_TRAINING_2_ADDR, reg); + } + } + + DEBUG_PBS_C("DDR3 - PBS TX - values for current pattern - ", + pattern_idx, 1); + for (pup = 0; pup < max_pup; pup++) { + /* + * To minimize delay elements, inc from pbs value the + * min pbs val + */ + DEBUG_PBS_S("DDR3 - PBS - PUP"); + DEBUG_PBS_D(pup, 1); + DEBUG_PBS_S(": "); + + for (dq = 0; dq < DQ_NUM; dq++) { + /* set skew value for all dq */ + /* Bit# Deskew <- Bit# Deskew - last / first failing bit Deskew For all bits (per PUP) (minimize delay elements) */ + DEBUG_PBS_S("DQ"); + DEBUG_PBS_D(dq, 1); + DEBUG_PBS_S("-"); + DEBUG_PBS_D(skew_sum_array[pup][dq] / + COUNT_PBS_REPEAT, 2); + DEBUG_PBS_S(", "); + } + DEBUG_PBS_S("\n"); + } + + /* + * Calculate the average skew for current pattern for each + * pup and each bit + */ + DEBUG_PBS_C("DDR3 - PBS TX - Average for pattern - ", + pattern_idx, 1); + + for (pup = 0; pup < max_pup; pup++) { + /* + * FOR ECC only :: found min and max value for current + * pattern skew array + */ + /* Loop for all dqs */ + for (dq = 0; dq < DQ_NUM; dq++) { + pattern_skew_array[pup][dq] += + (skew_sum_array[pup][dq] / + COUNT_PBS_REPEAT); + } + } + } + + /* Calculate the average skew */ + for (pup = 0; pup < max_pup; pup++) { + for (dq = 0; dq < DQ_NUM; dq++) + skew_array[((pup) * DQ_NUM) + dq] = + pattern_skew_array[pup][dq] / COUNT_PBS_PATTERN; + } + + DEBUG_PBS_S("DDR3 - PBS TX - Average for all patterns:\n"); + for (pup = 0; pup < max_pup; pup++) { + /* + * To minimize delay elements, inc from pbs value the min + * pbs val + */ + DEBUG_PBS_S("DDR3 - PBS - PUP"); + DEBUG_PBS_D(pup, 1); + DEBUG_PBS_S(": "); + + for (dq = 0; dq < DQ_NUM; dq++) { + /* Set skew value for all dq */ + /* + * Bit# Deskew <- Bit# Deskew - last / first + * failing bit Deskew For all bits (per PUP) + * (minimize delay elements) + */ + DEBUG_PBS_S("DQ"); + DEBUG_PBS_D(dq, 1); + DEBUG_PBS_S("-"); + DEBUG_PBS_D(skew_array[(pup * DQ_NUM) + dq], 2); + DEBUG_PBS_S(", "); + } + DEBUG_PBS_S("\n"); + } + + /* Return ADLL to default value */ + for (pup = 0; pup < max_pup; pup++) { + if (pup == (max_pup - 1) && dram_info->ecc_ena) + pup = ECC_PUP; + ddr3_pbs_write_pup_dqs_reg(CS0, pup, INIT_WL_DELAY); + } + + /* Set averaged PBS results */ + ddr3_set_pbs_results(dram_info, 1); + + /* Disable SW override - Must be in a different stage */ + /* [0]=0 - Enable SW override */ + reg = reg_read(REG_DRAM_TRAINING_2_ADDR); + reg &= ~(1 << REG_DRAM_TRAINING_2_SW_OVRD_OFFS); + /* 0x15B8 - Training SW 2 Register */ + reg_write(REG_DRAM_TRAINING_2_ADDR, reg); + + reg = reg_read(REG_DRAM_TRAINING_1_ADDR) | + (1 << REG_DRAM_TRAINING_1_TRNBPOINT_OFFS); + reg_write(REG_DRAM_TRAINING_1_ADDR, reg); + + DEBUG_PBS_S("DDR3 - PBS Tx - PBS TX ended successfuly\n"); + + return MV_OK; +} + +/* + * Name: ddr3_tx_shift_dqs_adll_step_before_fail + * Desc: Execute the Tx shift DQ phase. + * Args: dram_info ddr3 training information struct + * cur_pup bit array of the function active pups. + * pbs_pattern_idx Index of PBS pattern + * Notes: + * Returns: MV_OK if success, other error code if fail. + */ +static int ddr3_tx_shift_dqs_adll_step_before_fail(MV_DRAM_INFO *dram_info, + u32 cur_pup, + u32 pbs_pattern_idx, u32 ecc) +{ + u32 unlock_pup; /* bit array of unlock pups */ + u32 new_lockup_pup; /* bit array of compare failed pups */ + u32 adll_val = 4; /* INIT_WL_DELAY */ + u32 cur_max_pup, pup; + u32 dqs_dly_set[MAX_PUP_NUM] = { 0 }; + u32 *pattern_ptr; + + /* Choose pattern */ + switch (dram_info->ddr_width) { +#if defined(MV88F672X) + case 16: + pattern_ptr = (u32 *)&pbs_pattern[pbs_pattern_idx]; + break; +#endif + case 32: + pattern_ptr = (u32 *)&pbs_pattern_32b[pbs_pattern_idx]; + break; +#if defined(MV88F78X60) + case 64: + pattern_ptr = (u32 *)&pbs_pattern_64b[pbs_pattern_idx]; + break; +#endif + default: + return MV_FAIL; + } + + /* Set current pup number */ + if (cur_pup == 0x1) /* Ecc mode */ + cur_max_pup = 1; + else + cur_max_pup = dram_info->num_of_std_pups; + + unlock_pup = cur_pup; /* '1' for each unlocked pup */ + + /* Loop on all ADLL Vaules */ + do { + /* Loop until found first fail */ + adll_val++; + + /* + * Increment (Move to right - ADLL) DQ TX delay + * (broadcast to all Data PUPs) + */ + for (pup = 0; pup < cur_max_pup; pup++) + ddr3_pbs_write_pup_dqs_reg(CS0, + pup * (1 - ecc) + + ECC_PUP * ecc, adll_val); + + /* + * Write and Read, compare results (read was already verified) + */ + /* 0 - all locked */ + new_lockup_pup = 0; + + if (MV_OK != ddr3_sdram_compare(dram_info, unlock_pup, + &new_lockup_pup, + pattern_ptr, LEN_PBS_PATTERN, + SDRAM_PBS_TX_OFFS, 1, 0, + NULL, + 0)) + return MV_FAIL; + + unlock_pup &= ~new_lockup_pup; + + DEBUG_PBS_FULL_S("Shift DQS by 2 steps for PUPs: "); + DEBUG_PBS_FULL_D(unlock_pup, 2); + DEBUG_PBS_FULL_C(", Set ADLL value = ", adll_val, 2); + + /* If any PUP failed there is '1' to mark the PUP */ + if (new_lockup_pup != 0) { + /* + * Decrement (Move Back to Left two steps - ADLL) + * DQ TX delay for current failed pups and save + */ + for (pup = 0; pup < cur_max_pup; pup++) { + if (((new_lockup_pup >> pup) & 0x1) && + dqs_dly_set[pup] == 0) + dqs_dly_set[pup] = adll_val - 1; + } + } + } while ((unlock_pup != 0) && (adll_val != ADLL_MAX)); + + if (unlock_pup != 0) { + DEBUG_PBS_FULL_S("DDR3 - PBS Tx - Shift DQ - Adll value reached maximum\n"); + + for (pup = 0; pup < cur_max_pup; pup++) { + if (((unlock_pup >> pup) & 0x1) && + dqs_dly_set[pup] == 0) + dqs_dly_set[pup] = adll_val - 1; + } + } + + DEBUG_PBS_FULL_C("PBS TX one step before fail last pups locked Adll ", + adll_val - 2, 2); + + /* Set the PUP DQS DLY Values */ + for (pup = 0; pup < cur_max_pup; pup++) + ddr3_pbs_write_pup_dqs_reg(CS0, pup * (1 - ecc) + ECC_PUP * ecc, + dqs_dly_set[pup]); + + /* Found one phase before fail */ + return MV_OK; +} + +/* + * Name: ddr3_pbs_rx + * Desc: Execute the PBS RX phase. + * Args: dram_info ddr3 training information struct + * Notes: + * Returns: MV_OK if success, other error code if fail. + */ +int ddr3_pbs_rx(MV_DRAM_INFO *dram_info) +{ + /* + * Array to hold the total sum of skew from all iterations + * (for average purpose) + */ + u32 skew_sum_array[MAX_PUP_NUM][DQ_NUM] = { {0} }; + + /* + * Array to hold the total average skew from both patterns + * (for average purpose) + */ + u32 pattern_skew_array[MAX_PUP_NUM][DQ_NUM] = { {0} }; + + u32 pbs_rep_time = 0; /* counts number of loop in case of fail */ + /* bit array for unlock pups - used to repeat on the RX operation */ + u32 cur_pup; + u32 max_pup; + u32 pbs_retry; + u32 pup, dq, pups, cur_max_pup, valid_pup, reg; + u32 pattern_idx; + u32 ecc; + /* indicates whether we need to start the loop again */ + int start_over; + int status; + + DEBUG_PBS_S("DDR3 - PBS RX - Starting PBS RX procedure\n"); + + pups = dram_info->num_of_total_pups; + max_pup = dram_info->num_of_total_pups; + + /* Enable SW override */ + reg = reg_read(REG_DRAM_TRAINING_2_ADDR) | + (1 << REG_DRAM_TRAINING_2_SW_OVRD_OFFS); + /* [0] = 1 - Enable SW override */ + /* 0x15B8 - Training SW 2 Register */ + reg_write(REG_DRAM_TRAINING_2_ADDR, reg); + DEBUG_PBS_FULL_S("DDR3 - PBS RX - SW Override Enabled\n"); + + reg = 1 << REG_DRAM_TRAINING_AUTO_OFFS; + reg_write(REG_DRAM_TRAINING_ADDR, reg); /* 0x15B0 - Training Register */ + + /* Running twice for 2 different patterns. each patterns - 3 times */ + for (pattern_idx = 0; pattern_idx < COUNT_PBS_PATTERN; pattern_idx++) { + DEBUG_PBS_FULL_C("DDR3 - PBS RX - Working with pattern - ", + pattern_idx, 1); + + /* Reset sum array */ + for (pup = 0; pup < pups; pup++) { + for (dq = 0; dq < DQ_NUM; dq++) + skew_sum_array[pup][dq] = 0; + } + + /* + * Perform PBS several of times (3 for each pattern). + * At the end, we'll use the average + */ + /* If there is ECC, do each PBS again with mux change */ + for (pbs_retry = 0; pbs_retry < COUNT_PBS_REPEAT; pbs_retry++) { + for (ecc = 0; ecc < (dram_info->ecc_ena + 1); ecc++) { + /* + * This parameter stores the current PUP + * num - ecc mode dependent - 4-8 / 1 pups + */ + cur_max_pup = (1 - ecc) * + dram_info->num_of_std_pups + ecc; + + if (ecc) { + /* Only 1 pup in this case */ + valid_pup = 0x1; + } else if (cur_max_pup > 4) { + /* 64 bit - 8 pups */ + valid_pup = 0xFF; + } else if (cur_max_pup == 4) { + /* 32 bit - 4 pups */ + valid_pup = 0xF; + } else { + /* 16 bit - 2 pups */ + valid_pup = 0x3; + } + + /* ECC Support - Switch ECC Mux on ecc=1 */ + reg = reg_read(REG_DRAM_TRAINING_2_ADDR) & + ~(1 << REG_DRAM_TRAINING_2_ECC_MUX_OFFS); + reg |= (dram_info->ecc_ena * ecc << + REG_DRAM_TRAINING_2_ECC_MUX_OFFS); + reg_write(REG_DRAM_TRAINING_2_ADDR, reg); + + if (ecc) + DEBUG_PBS_FULL_S("DDR3 - PBS Rx - ECC Mux Enabled\n"); + else + DEBUG_PBS_FULL_S("DDR3 - PBS Rx - ECC Mux Disabled\n"); + + /* Init iteration values */ + /* Clear the locked DQs */ + for (pup = 0; pup < cur_max_pup; pup++) { + for (dq = 0; dq < DQ_NUM; dq++) { + pbs_locked_dq[ + pup + ecc * (max_pup - 1)][dq] = + 0; + } + } + + pbs_rep_time = 0; + cur_pup = valid_pup; + start_over = 0; + + /* + * Run loop On current Pattern and current + * pattern iteration (just to cover the false + * fail problem + */ + do { + DEBUG_PBS_FULL_S("DDR3 - PBS Rx - Pbs Rep Loop is "); + DEBUG_PBS_FULL_D(pbs_rep_time, 1); + DEBUG_PBS_FULL_S(", for Retry No."); + DEBUG_PBS_FULL_D(pbs_retry, 1); + DEBUG_PBS_FULL_S("\n"); + + /* Set all PBS values to MAX (31) */ + for (pup = 0; pup < cur_max_pup; pup++) { + for (dq = 0; dq < DQ_NUM; dq++) + ddr3_write_pup_reg( + PUP_PBS_RX + + pbs_dq_mapping[ + pup * (1 - ecc) + + ecc * ECC_PUP] + [dq], CS0, + pup + ecc * ECC_PUP, + 0, MAX_PBS); + } + + /* Set all DQS PBS values to MIN (0) */ + for (pup = 0; pup < cur_max_pup; pup++) { + ddr3_write_pup_reg(PUP_PBS_RX + + DQ_NUM, CS0, + pup + + ecc * + ECC_PUP, 0, + 0); + } + + /* Shift DQS, To first Fail */ + DEBUG_PBS_FULL_S("DDR3 - PBS Rx - Shift RX DQS to first fail\n"); + + status = ddr3_rx_shift_dqs_to_first_fail + (dram_info, cur_pup, + pattern_idx, ecc); + if (MV_OK != status) { + DEBUG_PBS_S("DDR3 - PBS Rx - ddr3_rx_shift_dqs_to_first_fail failed.\n"); + DEBUG_PBS_D(status, 8); + DEBUG_PBS_S("\nDDR3 - PBS Rx - SKIP.\n"); + + /* Reset read FIFO */ + reg = reg_read(REG_DRAM_TRAINING_ADDR); + /* Start Auto Read Leveling procedure */ + reg |= (1 << REG_DRAM_TRAINING_RL_OFFS); + /* 0x15B0 - Training Register */ + reg_write(REG_DRAM_TRAINING_ADDR, reg); + + reg = reg_read(REG_DRAM_TRAINING_2_ADDR); + reg |= ((1 << REG_DRAM_TRAINING_2_FIFO_RST_OFFS) + + (1 << REG_DRAM_TRAINING_2_SW_OVRD_OFFS)); + /* [0] = 1 - Enable SW override, [4] = 1 - FIFO reset */ + /* 0x15B8 - Training SW 2 Register */ + reg_write(REG_DRAM_TRAINING_2_ADDR, reg); + + do { + reg = (reg_read(REG_DRAM_TRAINING_2_ADDR)) + & (1 << REG_DRAM_TRAINING_2_FIFO_RST_OFFS); + } while (reg); /* Wait for '0' */ + + reg = reg_read(REG_DRAM_TRAINING_ADDR); + /* Clear Auto Read Leveling procedure */ + reg &= ~(1 << REG_DRAM_TRAINING_RL_OFFS); + /* 0x15B0 - Training Register */ + reg_write(REG_DRAM_TRAINING_ADDR, reg); + + /* Set ADLL to 15 */ + for (pup = 0; pup < max_pup; + pup++) { + ddr3_write_pup_reg + (PUP_DQS_RD, CS0, + pup + + (ecc * ECC_PUP), 0, + 15); + } + + /* Set all PBS values to MIN (0) */ + for (pup = 0; pup < cur_max_pup; + pup++) { + for (dq = 0; + dq < DQ_NUM; dq++) + ddr3_write_pup_reg + (PUP_PBS_RX + + pbs_dq_mapping + [pup * (1 - ecc) + + ecc * ECC_PUP] + [dq], CS0, + pup + ecc * ECC_PUP, + 0, MIN_PBS); + } + + return MV_OK; + } + + /* PBS For each bit */ + DEBUG_PBS_FULL_S("DDR3 - PBS Rx - perform PBS for each bit\n"); + /* in this stage - start_over = 0; */ + if (MV_OK != ddr3_pbs_per_bit( + dram_info, &start_over, + 0, &cur_pup, + pattern_idx, ecc)) { + DEBUG_PBS_S("DDR3 - PBS Rx - ddr3_pbs_per_bit failed."); + return MV_DDR3_TRAINING_ERR_PBS_RX_PER_BIT; + } + + } while ((start_over == 1) && + (++pbs_rep_time < COUNT_PBS_STARTOVER)); + + if (pbs_rep_time == COUNT_PBS_STARTOVER && + start_over == 1) { + DEBUG_PBS_FULL_S("DDR3 - PBS Rx - FAIL - Algorithm failed doing RX PBS\n"); + return MV_DDR3_TRAINING_ERR_PBS_RX_MAX_VAL; + } + + /* Return DQS ADLL to default value - 15 */ + /* Set all DQS PBS values to MIN (0) */ + for (pup = 0; pup < cur_max_pup; pup++) + ddr3_write_pup_reg(PUP_DQS_RD, CS0, + pup + ecc * ECC_PUP, + 0, INIT_RL_DELAY); + + DEBUG_PBS_FULL_C("DDR3 - PBS RX - values for iteration - ", + pbs_retry, 1); + for (pup = 0; pup < cur_max_pup; pup++) { + /* + * To minimize delay elements, inc from + * pbs value the min pbs val + */ + DEBUG_PBS_FULL_S("DDR3 - PBS - PUP"); + DEBUG_PBS_FULL_D((pup + + (ecc * ECC_PUP)), 1); + DEBUG_PBS_FULL_S(": "); + + for (dq = 0; dq < DQ_NUM; dq++) { + /* Set skew value for all dq */ + /* + * Bit# Deskew <- Bit# Deskew - + * last / first failing bit + * Deskew For all bits (per PUP) + * (minimize delay elements) + */ + DEBUG_PBS_FULL_S("DQ"); + DEBUG_PBS_FULL_D(dq, 1); + DEBUG_PBS_FULL_S("-"); + DEBUG_PBS_FULL_D(skew_array + [((pup) * + DQ_NUM) + + dq], 2); + DEBUG_PBS_FULL_S(", "); + } + DEBUG_PBS_FULL_S("\n"); + } + + /* + * Collect the results we got on this trial + * of PBS + */ + for (pup = 0; pup < cur_max_pup; pup++) { + for (dq = 0; dq < DQ_NUM; dq++) { + skew_sum_array + [pup + (ecc * (max_pup - 1))] + [dq] += + skew_array[((pup) * DQ_NUM) + dq]; + } + } + + /* ECC Support - Disable ECC MUX */ + reg = reg_read(REG_DRAM_TRAINING_2_ADDR) & + ~(1 << REG_DRAM_TRAINING_2_ECC_MUX_OFFS); + reg_write(REG_DRAM_TRAINING_2_ADDR, reg); + } + } + + /* + * Calculate the average skew for current pattern for each + * pup and each bit + */ + DEBUG_PBS_FULL_C("DDR3 - PBS RX - Average for pattern - ", + pattern_idx, 1); + for (pup = 0; pup < max_pup; pup++) { + /* + * FOR ECC only :: found min and max value for + * current pattern skew array + */ + /* Loop for all dqs */ + for (dq = 0; dq < DQ_NUM; dq++) { + pattern_skew_array[pup][dq] += + (skew_sum_array[pup][dq] / + COUNT_PBS_REPEAT); + } + } + + DEBUG_PBS_C("DDR3 - PBS RX - values for current pattern - ", + pattern_idx, 1); + for (pup = 0; pup < max_pup; pup++) { + /* + * To minimize delay elements, inc from pbs value the + * min pbs val + */ + DEBUG_PBS_S("DDR3 - PBS RX - PUP"); + DEBUG_PBS_D(pup, 1); + DEBUG_PBS_S(": "); + + for (dq = 0; dq < DQ_NUM; dq++) { + /* Set skew value for all dq */ + /* + * Bit# Deskew <- Bit# Deskew - last / first + * failing bit Deskew For all bits (per PUP) + * (minimize delay elements) + */ + DEBUG_PBS_S("DQ"); + DEBUG_PBS_D(dq, 1); + DEBUG_PBS_S("-"); + DEBUG_PBS_D(skew_sum_array[pup][dq] / + COUNT_PBS_REPEAT, 2); + DEBUG_PBS_S(", "); + } + DEBUG_PBS_S("\n"); + } + } + + /* Calculate the average skew */ + for (pup = 0; pup < max_pup; pup++) { + for (dq = 0; dq < DQ_NUM; dq++) + skew_array[((pup) * DQ_NUM) + dq] = + pattern_skew_array[pup][dq] / COUNT_PBS_PATTERN; + } + + DEBUG_PBS_S("DDR3 - PBS RX - Average for all patterns:\n"); + for (pup = 0; pup < max_pup; pup++) { + /* + * To minimize delay elements, inc from pbs value the + * min pbs val + */ + DEBUG_PBS_S("DDR3 - PBS - PUP"); + DEBUG_PBS_D(pup, 1); + DEBUG_PBS_S(": "); + + for (dq = 0; dq < DQ_NUM; dq++) { + /* Set skew value for all dq */ + /* + * Bit# Deskew <- Bit# Deskew - last / first + * failing bit Deskew For all bits (per PUP) + * (minimize delay elements) + */ + DEBUG_PBS_S("DQ"); + DEBUG_PBS_D(dq, 1); + DEBUG_PBS_S("-"); + DEBUG_PBS_D(skew_array[(pup * DQ_NUM) + dq], 2); + DEBUG_PBS_S(", "); + } + DEBUG_PBS_S("\n"); + } + + /* Return ADLL to default value */ + ddr3_write_pup_reg(PUP_DQS_RD, CS0, PUP_BC, 0, INIT_RL_DELAY); + + /* Set averaged PBS results */ + ddr3_set_pbs_results(dram_info, 0); + + /* Disable SW override - Must be in a different stage */ + /* [0]=0 - Enable SW override */ + reg = reg_read(REG_DRAM_TRAINING_2_ADDR); + reg &= ~(1 << REG_DRAM_TRAINING_2_SW_OVRD_OFFS); + /* 0x15B8 - Training SW 2 Register */ + reg_write(REG_DRAM_TRAINING_2_ADDR, reg); + + reg = reg_read(REG_DRAM_TRAINING_1_ADDR) | + (1 << REG_DRAM_TRAINING_1_TRNBPOINT_OFFS); + reg_write(REG_DRAM_TRAINING_1_ADDR, reg); + + DEBUG_PBS_FULL_S("DDR3 - PBS RX - ended successfuly\n"); + + return MV_OK; +} + +/* + * Name: ddr3_rx_shift_dqs_to_first_fail + * Desc: Execute the Rx shift DQ phase. + * Args: dram_info ddr3 training information struct + * cur_pup bit array of the function active pups. + * pbs_pattern_idx Index of PBS pattern + * Notes: + * Returns: MV_OK if success, other error code if fail. + */ +static int ddr3_rx_shift_dqs_to_first_fail(MV_DRAM_INFO *dram_info, u32 cur_pup, + u32 pbs_pattern_idx, u32 ecc) +{ + u32 unlock_pup; /* bit array of unlock pups */ + u32 new_lockup_pup; /* bit array of compare failed pups */ + u32 adll_val = MAX_DELAY; + u32 dqs_deskew_val = 0; /* current value of DQS PBS deskew */ + u32 cur_max_pup, pup, pass_pup; + u32 *pattern_ptr; + + /* Choose pattern */ + switch (dram_info->ddr_width) { +#if defined(MV88F672X) + case 16: + pattern_ptr = (u32 *)&pbs_pattern[pbs_pattern_idx]; + break; +#endif + case 32: + pattern_ptr = (u32 *)&pbs_pattern_32b[pbs_pattern_idx]; + break; +#if defined(MV88F78X60) + case 64: + pattern_ptr = (u32 *)&pbs_pattern_64b[pbs_pattern_idx]; + break; +#endif + default: + return MV_FAIL; + } + + /* Set current pup number */ + if (cur_pup == 0x1) /* Ecc mode */ + cur_max_pup = 1; + else + cur_max_pup = dram_info->num_of_std_pups; + + unlock_pup = cur_pup; /* '1' for each unlocked pup */ + + DEBUG_PBS_FULL_S("DDR3 - PBS RX - Shift DQS - Starting...\n"); + + /* Set DQS ADLL to MAX */ + DEBUG_PBS_FULL_S("DDR3 - PBS RX - Shift DQS - Set DQS ADLL to Max for all PUPs\n"); + for (pup = 0; pup < cur_max_pup; pup++) + ddr3_write_pup_reg(PUP_DQS_RD, CS0, pup + ecc * ECC_PUP, 0, + MAX_DELAY); + + /* Loop on all ADLL Vaules */ + do { + /* Loop until found fail for all pups */ + new_lockup_pup = 0; + if (MV_OK != ddr3_sdram_compare(dram_info, unlock_pup, + &new_lockup_pup, + pattern_ptr, LEN_PBS_PATTERN, + SDRAM_PBS_I_OFFS + + pbs_pattern_idx * SDRAM_PBS_NEXT_OFFS, + 0, 0, NULL, 0)) { + DEBUG_PBS_S("DDR3 - PBS Rx - Shift DQS - MV_DDR3_TRAINING_ERR_PBS_SHIFT_QDS_SRAM_CMP(ddr3_sdram_compare)\n"); + return MV_DDR3_TRAINING_ERR_PBS_SHIFT_QDS_SRAM_CMP; + } + + if ((new_lockup_pup != 0) && (dqs_deskew_val <= 1)) { + /* Fail on start with first deskew value */ + /* Decrement DQS ADLL */ + --adll_val; + if (adll_val == ADLL_MIN) { + DEBUG_PBS_S("DDR3 - PBS Rx - Shift DQS - fail on start with first deskew value\n"); + return MV_DDR3_TRAINING_ERR_PBS_SHIFT_QDS_SRAM_CMP; + } + ddr3_write_pup_reg(PUP_DQS_RD, CS0, pup + ecc * ECC_PUP, + 0, adll_val); + continue; + } + + /* Update all new locked pups */ + unlock_pup &= ~new_lockup_pup; + + if ((unlock_pup == 0) || (dqs_deskew_val == MAX_PBS)) { + if (dqs_deskew_val == MAX_PBS) { + /* + * Reach max value of dqs deskew or get fail + * for all pups + */ + DEBUG_PBS_FULL_S("DDR3 - PBS RX - Shift DQS - DQS deskew reached maximum value\n"); + } + break; + } + + DEBUG_PBS_FULL_S("DDR3 - PBS RX - Shift DQS - Inc DQS deskew for PUPs: "); + DEBUG_PBS_FULL_D(unlock_pup, 2); + DEBUG_PBS_FULL_C(", deskew = ", dqs_deskew_val, 2); + + /* Increment DQS deskew elements - Only for unlocked pups */ + dqs_deskew_val++; + for (pup = 0; pup < cur_max_pup; pup++) { + if (IS_PUP_ACTIVE(unlock_pup, pup) == 1) { + ddr3_write_pup_reg(PUP_PBS_RX + DQS_DQ_NUM, CS0, + pup + ecc * ECC_PUP, 0, + dqs_deskew_val); + } + } + } while (1); + + DEBUG_PBS_FULL_S("DDR3 - PBS RX - Shift DQS - ADLL shift one step before fail\n"); + /* Continue to ADLL shift one step before fail */ + unlock_pup = cur_pup; + do { + /* Loop until pass compare for all pups */ + new_lockup_pup = 0; + /* Read and compare results */ + if (MV_OK != ddr3_sdram_compare(dram_info, unlock_pup, &new_lockup_pup, + pattern_ptr, LEN_PBS_PATTERN, + SDRAM_PBS_I_OFFS + + pbs_pattern_idx * SDRAM_PBS_NEXT_OFFS, + 1, 0, NULL, 0)) { + DEBUG_PBS_S("DDR3 - PBS Rx - Shift DQS - MV_DDR3_TRAINING_ERR_PBS_SHIFT_QDS_SRAM_CMP(ddr3_sdram_compare)\n"); + return MV_DDR3_TRAINING_ERR_PBS_SHIFT_QDS_SRAM_CMP; + } + + /* + * Get mask for pup which passed so their adll will be + * changed to 2 steps before fails + */ + pass_pup = unlock_pup & ~new_lockup_pup; + + DEBUG_PBS_FULL_S("Shift DQS by 2 steps for PUPs: "); + DEBUG_PBS_FULL_D(pass_pup, 2); + DEBUG_PBS_FULL_C(", Set ADLL value = ", (adll_val - 2), 2); + + /* Only for pass pups */ + for (pup = 0; pup < cur_max_pup; pup++) { + if (IS_PUP_ACTIVE(pass_pup, pup) == 1) { + ddr3_write_pup_reg(PUP_DQS_RD, CS0, + pup + ecc * ECC_PUP, 0, + (adll_val - 2)); + } + } + + /* Locked pups that compare success */ + unlock_pup &= new_lockup_pup; + + if (unlock_pup == 0) { + /* All pups locked */ + break; + } + + /* Found error */ + if (adll_val == 0) { + DEBUG_PBS_FULL_S("DDR3 - PBS Rx - Shift DQS - Adll reach min value\n"); + return MV_DDR3_TRAINING_ERR_PBS_SHIFT_QDS_MAX_VAL; + } + + /* + * Decrement (Move Back to Left one phase - ADLL) dqs RX delay + */ + adll_val--; + for (pup = 0; pup < cur_max_pup; pup++) { + if (IS_PUP_ACTIVE(unlock_pup, pup) == 1) { + ddr3_write_pup_reg(PUP_DQS_RD, CS0, + pup + ecc * ECC_PUP, 0, + adll_val); + } + } + } while (1); + + return MV_OK; +} + +/* + * lock_pups() extracted from ddr3_pbs_per_bit(). This just got too + * much indented making it hard to read / edit. + */ +static void lock_pups(u32 pup, u32 *pup_locked, u8 *unlock_pup_dq_array, + u32 pbs_curr_val, u32 start_pbs, u32 ecc, int is_tx) +{ + u32 dq; + int idx; + + /* Lock PBS value for all remaining PUPs bits */ + DEBUG_PBS_FULL_S("DDR3 - PBS Per bit - Lock PBS value for all remaining PUPs bits, pup "); + DEBUG_PBS_FULL_D(pup, 1); + DEBUG_PBS_FULL_C(" pbs value ", pbs_curr_val, 2); + + idx = pup * (1 - ecc) + ecc * ECC_PUP; + *pup_locked &= ~(1 << pup); + + for (dq = 0; dq < DQ_NUM; dq++) { + if (IS_PUP_ACTIVE(unlock_pup_dq_array[dq], pup) == 1) { + int offs; + + /* Lock current dq */ + unlock_pup_dq_array[dq] &= ~(1 << pup); + skew_array[(pup * DQ_NUM) + dq] = pbs_curr_val; + + if (is_tx == 1) + offs = PUP_PBS_TX; + else + offs = PUP_PBS_RX; + + ddr3_write_pup_reg(offs + + pbs_dq_mapping[idx][dq], CS0, + idx, 0, start_pbs); + } + } +} + +/* + * Name: ddr3_pbs_per_bit + * Desc: Execute the Per Bit Skew phase. + * Args: start_over Return whether need to start over the algorithm + * is_tx Indicate whether Rx or Tx + * pcur_pup bit array of the function active pups. return the + * pups that need to repeat on the PBS + * pbs_pattern_idx Index of PBS pattern + * + * Notes: Current implementation supports double activation of this function. + * i.e. in order to activate this function (using start_over) more than + * twice, the implementation should change. + * imlementation limitation are marked using + * ' CHIP-ONLY! - Implementation Limitation ' + * Returns: MV_OK if success, other error code if fail. + */ +static int ddr3_pbs_per_bit(MV_DRAM_INFO *dram_info, int *start_over, int is_tx, + u32 *pcur_pup, u32 pbs_pattern_idx, u32 ecc) +{ + /* + * Bit array to indicate if we already get fail on bit per pup & dq bit + */ + u8 unlock_pup_dq_array[DQ_NUM] = { + *pcur_pup, *pcur_pup, *pcur_pup, *pcur_pup, *pcur_pup, + *pcur_pup, *pcur_pup, *pcur_pup + }; + + u8 cmp_unlock_pup_dq_array[COUNT_PBS_COMP_RETRY_NUM][DQ_NUM]; + u32 pup, dq; + /* value of pbs is according to RX or TX */ + u32 start_pbs, last_pbs; + u32 pbs_curr_val; + /* bit array that indicates all dq of the pup locked */ + u32 pup_locked; + u32 first_fail[MAX_PUP_NUM] = { 0 }; /* count first fail per pup */ + /* indicates whether we get first fail per pup */ + int first_failed[MAX_PUP_NUM] = { 0 }; + /* bit array that indicates pup already get fail */ + u32 sum_pup_fail; + /* use to calculate diff between curr pbs to first fail pbs */ + u32 calc_pbs_diff; + u32 pbs_cmp_retry; + u32 max_pup; + + /* Set init values for retry array - 8 retry */ + for (pbs_cmp_retry = 0; pbs_cmp_retry < COUNT_PBS_COMP_RETRY_NUM; + pbs_cmp_retry++) { + for (dq = 0; dq < DQ_NUM; dq++) + cmp_unlock_pup_dq_array[pbs_cmp_retry][dq] = *pcur_pup; + } + + memset(&skew_array, 0, MAX_PUP_NUM * DQ_NUM * sizeof(u32)); + + DEBUG_PBS_FULL_S("DDR3 - PBS Per bit - Started\n"); + + /* The pbs value depends if rx or tx */ + if (is_tx == 1) { + start_pbs = MIN_PBS; + last_pbs = MAX_PBS; + } else { + start_pbs = MAX_PBS; + last_pbs = MIN_PBS; + } + + pbs_curr_val = start_pbs; + pup_locked = *pcur_pup; + + /* Set current pup number */ + if (pup_locked == 0x1) /* Ecc mode */ + max_pup = 1; + else + max_pup = dram_info->num_of_std_pups; + + do { + /* Increment/ decrement PBS for un-lock bits only */ + if (is_tx == 1) + pbs_curr_val++; + else + pbs_curr_val--; + + /* Set Current PBS delay */ + for (dq = 0; dq < DQ_NUM; dq++) { + /* Check DQ bits to see if locked in all pups */ + if (unlock_pup_dq_array[dq] == 0) { + DEBUG_PBS_FULL_S("DDR3 - PBS Per bit - All pups are locked for DQ "); + DEBUG_PBS_FULL_D(dq, 1); + DEBUG_PBS_FULL_S("\n"); + continue; + } + + for (pup = 0; pup < max_pup; pup++) { + int idx; + + idx = pup * (1 - ecc) + ecc * ECC_PUP; + + if (IS_PUP_ACTIVE(unlock_pup_dq_array[dq], pup) + == 0) + continue; + + if (is_tx == 1) + ddr3_write_pup_reg( + PUP_PBS_TX + pbs_dq_mapping[idx][dq], + CS0, idx, 0, pbs_curr_val); + else + ddr3_write_pup_reg( + PUP_PBS_RX + pbs_dq_mapping[idx][dq], + CS0, idx, 0, pbs_curr_val); + } + } + + /* + * Write Read and compare results - run the test + * DDR_PBS_COMP_RETRY_NUM times + */ + /* Run number of read and write to verify */ + for (pbs_cmp_retry = 0; + pbs_cmp_retry < COUNT_PBS_COMP_RETRY_NUM; + pbs_cmp_retry++) { + + if (MV_OK != + ddr3_sdram_pbs_compare(dram_info, pup_locked, is_tx, + pbs_pattern_idx, + pbs_curr_val, start_pbs, + skew_array, + cmp_unlock_pup_dq_array + [pbs_cmp_retry], ecc)) + return MV_FAIL; + + for (pup = 0; pup < max_pup; pup++) { + for (dq = 0; dq < DQ_NUM; dq++) { + if ((IS_PUP_ACTIVE(unlock_pup_dq_array[dq], + pup) == 1) + && (IS_PUP_ACTIVE(cmp_unlock_pup_dq_array + [pbs_cmp_retry][dq], + pup) == 0)) { + DEBUG_PBS_FULL_S("DDR3 - PBS Per bit - PbsCurrVal: "); + DEBUG_PBS_FULL_D(pbs_curr_val, 2); + DEBUG_PBS_FULL_S(" PUP: "); + DEBUG_PBS_FULL_D(pup, 1); + DEBUG_PBS_FULL_S(" DQ: "); + DEBUG_PBS_FULL_D(dq, 1); + DEBUG_PBS_FULL_S(" - failed\n"); + } + } + } + + for (dq = 0; dq < DQ_NUM; dq++) { + unlock_pup_dq_array[dq] &= + cmp_unlock_pup_dq_array[pbs_cmp_retry][dq]; + } + } + + pup_locked = 0; + sum_pup_fail = *pcur_pup; + + /* Check which DQ is failed */ + for (dq = 0; dq < DQ_NUM; dq++) { + /* Summarize the locked pup */ + pup_locked |= unlock_pup_dq_array[dq]; + + /* Check if get fail */ + sum_pup_fail &= unlock_pup_dq_array[dq]; + } + + /* If all PUPS are locked in all DQ - Break */ + if (pup_locked == 0) { + /* All pups are locked */ + *start_over = 0; + DEBUG_PBS_FULL_S("DDR3 - PBS Per bit - All bit in all pups are successfully locked\n"); + break; + } + + /* PBS deskew elements reach max ? */ + if (pbs_curr_val == last_pbs) { + DEBUG_PBS_FULL_S("DDR3 - PBS Per bit - PBS deskew elements reach max\n"); + /* CHIP-ONLY! - Implementation Limitation */ + *start_over = (sum_pup_fail != 0) && (!(*start_over)); + *pcur_pup = pup_locked; + + DEBUG_PBS_FULL_S("DDR3 - PBS Per bit - StartOver: "); + DEBUG_PBS_FULL_D(*start_over, 1); + DEBUG_PBS_FULL_S(" pup_locked: "); + DEBUG_PBS_FULL_D(pup_locked, 2); + DEBUG_PBS_FULL_S(" sum_pup_fail: "); + DEBUG_PBS_FULL_D(sum_pup_fail, 2); + DEBUG_PBS_FULL_S("\n"); + + /* Lock PBS value for all remaining bits */ + for (pup = 0; pup < max_pup; pup++) { + /* Check if current pup already received error */ + if (IS_PUP_ACTIVE(pup_locked, pup) == 1) { + /* Valid pup for current function */ + if (IS_PUP_ACTIVE(sum_pup_fail, pup) == + 1 && (*start_over == 1)) { + DEBUG_PBS_FULL_C("DDR3 - PBS Per bit - skipping lock of pup (first loop of pbs)", + pup, 1); + continue; + } else + if (IS_PUP_ACTIVE(sum_pup_fail, pup) + == 1) { + DEBUG_PBS_FULL_C("DDR3 - PBS Per bit - Locking pup %d (even though it wasn't supposed to be locked)", + pup, 1); + } + + /* Already got fail on the PUP */ + /* Lock PBS value for all remaining bits */ + DEBUG_PBS_FULL_S("DDR3 - PBS Per bit - Locking remaning DQs for pup - "); + DEBUG_PBS_FULL_D(pup, 1); + DEBUG_PBS_FULL_S(": "); + + for (dq = 0; dq < DQ_NUM; dq++) { + if (IS_PUP_ACTIVE + (unlock_pup_dq_array[dq], + pup) == 1) { + DEBUG_PBS_FULL_D(dq, 1); + DEBUG_PBS_FULL_S(","); + /* set current PBS */ + skew_array[((pup) * + DQ_NUM) + + dq] = + pbs_curr_val; + } + } + + if (*start_over == 1) { + /* + * Reset this pup bit - when + * restart the PBS, ignore this + * pup + */ + *pcur_pup &= ~(1 << pup); + } + DEBUG_PBS_FULL_S("\n"); + } else { + DEBUG_PBS_FULL_S("DDR3 - PBS Per bit - Pup "); + DEBUG_PBS_FULL_D(pup, 1); + DEBUG_PBS_FULL_C(" is not set in puplocked - ", + pup_locked, 1); + } + } + + /* Need to start the PBS again */ + if (*start_over == 1) { + DEBUG_PBS_FULL_S("DDR3 - PBS Per bit - false fail - returning to start\n"); + return MV_OK; + } + break; + } + + /* Diff Check */ + for (pup = 0; pup < max_pup; pup++) { + if (IS_PUP_ACTIVE(pup_locked, pup) == 1) { + /* pup is not locked */ + if (first_failed[pup] == 0) { + /* No first fail until now */ + if (IS_PUP_ACTIVE(sum_pup_fail, pup) == + 0) { + /* Get first fail */ + DEBUG_PBS_FULL_C("DDR3 - PBS Per bit - First fail in pup ", + pup, 1); + first_failed[pup] = 1; + first_fail[pup] = pbs_curr_val; + } + } else { + /* Already got first fail */ + if (is_tx == 1) { + /* TX - inc pbs */ + calc_pbs_diff = pbs_curr_val - + first_fail[pup]; + } else { + /* RX - dec pbs */ + calc_pbs_diff = first_fail[pup] - + pbs_curr_val; + } + + if (calc_pbs_diff >= PBS_DIFF_LIMIT) { + lock_pups(pup, &pup_locked, + unlock_pup_dq_array, + pbs_curr_val, + start_pbs, ecc, is_tx); + } + } + } + } + } while (1); + + return MV_OK; +} + +/* + * Name: ddr3_set_pbs_results + * Desc: Set to HW the PBS phase results. + * Args: is_tx Indicates whether to set Tx or RX results + * Notes: + * Returns: MV_OK if success, other error code if fail. + */ +static int ddr3_set_pbs_results(MV_DRAM_INFO *dram_info, int is_tx) +{ + u32 pup, phys_pup, dq; + u32 max_pup; /* number of valid pups */ + u32 pbs_min; /* minimal pbs val per pup */ + u32 pbs_max; /* maximum pbs val per pup */ + u32 val[9]; + + max_pup = dram_info->num_of_total_pups; + DEBUG_PBS_FULL_S("DDR3 - PBS - ddr3_set_pbs_results:\n"); + + /* Loop for all dqs & pups */ + for (pup = 0; pup < max_pup; pup++) { + if (pup == (max_pup - 1) && dram_info->ecc_ena) + phys_pup = ECC_PUP; + else + phys_pup = pup; + + /* + * To minimize delay elements, inc from pbs value the min + * pbs val + */ + pbs_min = MAX_PBS; + pbs_max = 0; + for (dq = 0; dq < DQ_NUM; dq++) { + if (pbs_min > skew_array[(pup * DQ_NUM) + dq]) + pbs_min = skew_array[(pup * DQ_NUM) + dq]; + + if (pbs_max < skew_array[(pup * DQ_NUM) + dq]) + pbs_max = skew_array[(pup * DQ_NUM) + dq]; + } + + pbs_max -= pbs_min; + + DEBUG_PBS_FULL_S("DDR3 - PBS - PUP"); + DEBUG_PBS_FULL_D(phys_pup, 1); + DEBUG_PBS_FULL_S(": Min Val = "); + DEBUG_PBS_FULL_D(pbs_min, 2); + DEBUG_PBS_FULL_C(", Max Val = ", pbs_max, 2); + + val[pup] = 0; + + for (dq = 0; dq < DQ_NUM; dq++) { + int idx; + int offs; + + /* Set skew value for all dq */ + /* + * Bit# Deskew <- Bit# Deskew - last / first + * failing bit Deskew For all bits (per PUP) + * (minimize delay elements) + */ + + DEBUG_PBS_FULL_S("DQ"); + DEBUG_PBS_FULL_D(dq, 1); + DEBUG_PBS_FULL_S("-"); + DEBUG_PBS_FULL_D((skew_array[(pup * DQ_NUM) + dq] - + pbs_min), 2); + DEBUG_PBS_FULL_S(", "); + + idx = (pup * DQ_NUM) + dq; + + if (is_tx == 1) + offs = PUP_PBS_TX; + else + offs = PUP_PBS_RX; + + ddr3_write_pup_reg(offs + pbs_dq_mapping[phys_pup][dq], + CS0, phys_pup, 0, + skew_array[idx] - pbs_min); + + if (is_tx == 1) + val[pup] += skew_array[idx] - pbs_min; + } + + DEBUG_PBS_FULL_S("\n"); + + /* Set the DQS the half of the Max PBS of the DQs */ + if (is_tx == 1) { + ddr3_write_pup_reg(PUP_PBS_TX + 8, CS0, phys_pup, 0, + pbs_max / 2); + ddr3_write_pup_reg(PUP_PBS_TX + 0xa, CS0, phys_pup, 0, + val[pup] / 8); + } else + ddr3_write_pup_reg(PUP_PBS_RX + 8, CS0, phys_pup, 0, + pbs_max / 2); + } + + return MV_OK; +} + +static void ddr3_pbs_write_pup_dqs_reg(u32 cs, u32 pup, u32 dqs_delay) +{ + u32 reg, delay; + + reg = (ddr3_read_pup_reg(PUP_WL_MODE, cs, pup) & 0x3FF); + delay = reg & PUP_DELAY_MASK; + reg |= ((dqs_delay + delay) << REG_PHY_DQS_REF_DLY_OFFS); + reg |= REG_PHY_REGISTRY_FILE_ACCESS_OP_WR; + reg |= (pup << REG_PHY_PUP_OFFS); + reg |= ((0x4 * cs + PUP_WL_MODE) << REG_PHY_CS_OFFS); + + reg_write(REG_PHY_REGISTRY_FILE_ACCESS_ADDR, reg); /* 0x16A0 */ + do { + reg = reg_read(REG_PHY_REGISTRY_FILE_ACCESS_ADDR) & + REG_PHY_REGISTRY_FILE_ACCESS_OP_DONE; + } while (reg); /* Wait for '0' to mark the end of the transaction */ + + udelay(10); +} + +/* + * Set training patterns + */ +int ddr3_load_pbs_patterns(MV_DRAM_INFO *dram_info) +{ + u32 cs, cs_count, cs_tmp; + u32 sdram_addr; + u32 *pattern_ptr0, *pattern_ptr1; + + /* Choose pattern */ + switch (dram_info->ddr_width) { +#if defined(MV88F672X) + case 16: + pattern_ptr0 = (u32 *)&pbs_pattern[0]; + pattern_ptr1 = (u32 *)&pbs_pattern[1]; + break; +#endif + case 32: + pattern_ptr0 = (u32 *)&pbs_pattern_32b[0]; + pattern_ptr1 = (u32 *)&pbs_pattern_32b[1]; + break; +#if defined(MV88F78X60) + case 64: + pattern_ptr0 = (u32 *)&pbs_pattern_64b[0]; + pattern_ptr1 = (u32 *)&pbs_pattern_64b[1]; + break; +#endif + default: + return MV_FAIL; + } + + /* Loop for each CS */ + for (cs = 0; cs < MAX_CS; cs++) { + if (dram_info->cs_ena & (1 << cs)) { + cs_count = 0; + for (cs_tmp = 0; cs_tmp < cs; cs_tmp++) { + if (dram_info->cs_ena & (1 << cs_tmp)) + cs_count++; + } + + /* Init PBS I pattern */ + sdram_addr = (cs_count * (SDRAM_CS_SIZE + 1) + + SDRAM_PBS_I_OFFS); + if (MV_OK != + ddr3_sdram_compare(dram_info, (u32) NULL, NULL, + pattern_ptr0, LEN_STD_PATTERN, + sdram_addr, 1, 0, NULL, + 0)) + return MV_FAIL; + + /* Init PBS II pattern */ + sdram_addr = (cs_count * (SDRAM_CS_SIZE + 1) + + SDRAM_PBS_II_OFFS); + if (MV_OK != + ddr3_sdram_compare(dram_info, (u32) NULL, NULL, + pattern_ptr1, LEN_STD_PATTERN, + sdram_addr, 1, 0, NULL, + 0)) + return MV_FAIL; + } + } + + return MV_OK; +} +#endif diff --git a/drivers/ddr/mvebu/ddr3_read_leveling.c b/drivers/ddr/mvebu/ddr3_read_leveling.c new file mode 100644 index 0000000..4662bde --- /dev/null +++ b/drivers/ddr/mvebu/ddr3_read_leveling.c @@ -0,0 +1,1214 @@ +/* + * Copyright (C) Marvell International Ltd. and its affiliates + * + * SPDX-License-Identifier: GPL-2.0 + */ + +#include <common.h> +#include <i2c.h> +#include <spl.h> +#include <asm/io.h> +#include <asm/arch/cpu.h> +#include <asm/arch/soc.h> + +#include "ddr3_hw_training.h" + +/* + * Debug + */ +#define DEBUG_RL_C(s, d, l) \ + DEBUG_RL_S(s); DEBUG_RL_D(d, l); DEBUG_RL_S("\n") +#define DEBUG_RL_FULL_C(s, d, l) \ + DEBUG_RL_FULL_S(s); DEBUG_RL_FULL_D(d, l); DEBUG_RL_FULL_S("\n") + +#ifdef MV_DEBUG_RL +#define DEBUG_RL_S(s) \ + debug_cond(ddr3_get_log_level() >= MV_LOG_LEVEL_2, "%s", s) +#define DEBUG_RL_D(d, l) \ + debug_cond(ddr3_get_log_level() >= MV_LOG_LEVEL_2, "%x", d) +#else +#define DEBUG_RL_S(s) +#define DEBUG_RL_D(d, l) +#endif + +#ifdef MV_DEBUG_RL_FULL +#define DEBUG_RL_FULL_S(s) puts(s) +#define DEBUG_RL_FULL_D(d, l) printf("%x", d) +#else +#define DEBUG_RL_FULL_S(s) +#define DEBUG_RL_FULL_D(d, l) +#endif + +extern u32 rl_pattern[LEN_STD_PATTERN]; + +#ifdef RL_MODE +static int ddr3_read_leveling_single_cs_rl_mode(u32 cs, u32 freq, + int ratio_2to1, u32 ecc, + MV_DRAM_INFO *dram_info); +#else +static int ddr3_read_leveling_single_cs_window_mode(u32 cs, u32 freq, + int ratio_2to1, u32 ecc, + MV_DRAM_INFO *dram_info); +#endif + +/* + * Name: ddr3_read_leveling_hw + * Desc: Execute the Read leveling phase by HW + * Args: dram_info - main struct + * freq - current sequence frequency + * Notes: + * Returns: MV_OK if success, MV_FAIL if fail. + */ +int ddr3_read_leveling_hw(u32 freq, MV_DRAM_INFO *dram_info) +{ + u32 reg; + + /* Debug message - Start Read leveling procedure */ + DEBUG_RL_S("DDR3 - Read Leveling - Starting HW RL procedure\n"); + + /* Start Auto Read Leveling procedure */ + reg = 1 << REG_DRAM_TRAINING_RL_OFFS; + /* Config the retest number */ + reg |= (COUNT_HW_RL << REG_DRAM_TRAINING_RETEST_OFFS); + + /* Enable CS in the automatic process */ + reg |= (dram_info->cs_ena << REG_DRAM_TRAINING_CS_OFFS); + + reg_write(REG_DRAM_TRAINING_ADDR, reg); /* 0x15B0 - Training Register */ + + reg = reg_read(REG_DRAM_TRAINING_SHADOW_ADDR) | + (1 << REG_DRAM_TRAINING_AUTO_OFFS); + reg_write(REG_DRAM_TRAINING_SHADOW_ADDR, reg); + + /* Wait */ + do { + reg = reg_read(REG_DRAM_TRAINING_SHADOW_ADDR) & + (1 << REG_DRAM_TRAINING_AUTO_OFFS); + } while (reg); /* Wait for '0' */ + + /* Check if Successful */ + if (reg_read(REG_DRAM_TRAINING_SHADOW_ADDR) & + (1 << REG_DRAM_TRAINING_ERROR_OFFS)) { + u32 delay, phase, pup, cs; + + dram_info->rl_max_phase = 0; + dram_info->rl_min_phase = 10; + + /* Read results to arrays */ + for (cs = 0; cs < MAX_CS; cs++) { + if (dram_info->cs_ena & (1 << cs)) { + for (pup = 0; + pup < dram_info->num_of_total_pups; + pup++) { + if (pup == dram_info->num_of_std_pups + && dram_info->ecc_ena) + pup = ECC_PUP; + reg = + ddr3_read_pup_reg(PUP_RL_MODE, cs, + pup); + phase = (reg >> REG_PHY_PHASE_OFFS) & + PUP_PHASE_MASK; + delay = reg & PUP_DELAY_MASK; + dram_info->rl_val[cs][pup][P] = phase; + if (phase > dram_info->rl_max_phase) + dram_info->rl_max_phase = phase; + if (phase < dram_info->rl_min_phase) + dram_info->rl_min_phase = phase; + dram_info->rl_val[cs][pup][D] = delay; + dram_info->rl_val[cs][pup][S] = + RL_FINAL_STATE; + reg = + ddr3_read_pup_reg(PUP_RL_MODE + 0x1, + cs, pup); + dram_info->rl_val[cs][pup][DQS] = + (reg & 0x3F); + } +#ifdef MV_DEBUG_RL + /* Print results */ + DEBUG_RL_C("DDR3 - Read Leveling - Results for CS - ", + (u32) cs, 1); + + for (pup = 0; + pup < (dram_info->num_of_total_pups); + pup++) { + if (pup == dram_info->num_of_std_pups + && dram_info->ecc_ena) + pup = ECC_PUP; + DEBUG_RL_S("DDR3 - Read Leveling - PUP: "); + DEBUG_RL_D((u32) pup, 1); + DEBUG_RL_S(", Phase: "); + DEBUG_RL_D((u32) dram_info-> + rl_val[cs][pup][P], 1); + DEBUG_RL_S(", Delay: "); + DEBUG_RL_D((u32) dram_info-> + rl_val[cs][pup][D], 2); + DEBUG_RL_S("\n"); + } +#endif + } + } + + dram_info->rd_rdy_dly = + reg_read(REG_READ_DATA_READY_DELAYS_ADDR) & + REG_READ_DATA_SAMPLE_DELAYS_MASK; + dram_info->rd_smpl_dly = + reg_read(REG_READ_DATA_SAMPLE_DELAYS_ADDR) & + REG_READ_DATA_READY_DELAYS_MASK; +#ifdef MV_DEBUG_RL + DEBUG_RL_C("DDR3 - Read Leveling - Read Sample Delay: ", + dram_info->rd_smpl_dly, 2); + DEBUG_RL_C("DDR3 - Read Leveling - Read Ready Delay: ", + dram_info->rd_rdy_dly, 2); + DEBUG_RL_S("DDR3 - Read Leveling - HW RL Ended Successfully\n"); +#endif + return MV_OK; + + } else { + DEBUG_RL_S("DDR3 - Read Leveling - HW RL Error\n"); + return MV_FAIL; + } +} + +/* + * Name: ddr3_read_leveling_sw + * Desc: Execute the Read leveling phase by SW + * Args: dram_info - main struct + * freq - current sequence frequency + * Notes: + * Returns: MV_OK if success, MV_FAIL if fail. + */ +int ddr3_read_leveling_sw(u32 freq, int ratio_2to1, MV_DRAM_INFO *dram_info) +{ + u32 reg, cs, ecc, pup_num, phase, delay, pup; + int status; + + /* Debug message - Start Read leveling procedure */ + DEBUG_RL_S("DDR3 - Read Leveling - Starting SW RL procedure\n"); + + /* Enable SW Read Leveling */ + reg = reg_read(REG_DRAM_TRAINING_2_ADDR) | + (1 << REG_DRAM_TRAINING_2_SW_OVRD_OFFS); + reg &= ~(1 << REG_DRAM_TRAINING_2_RL_MODE_OFFS); + /* [0]=1 - Enable SW override */ + /* 0x15B8 - Training SW 2 Register */ + reg_write(REG_DRAM_TRAINING_2_ADDR, reg); + +#ifdef RL_MODE + reg = (dram_info->cs_ena << REG_DRAM_TRAINING_CS_OFFS) | + (1 << REG_DRAM_TRAINING_AUTO_OFFS); + reg_write(REG_DRAM_TRAINING_ADDR, reg); /* 0x15B0 - Training Register */ +#endif + + /* Loop for each CS */ + for (cs = 0; cs < dram_info->num_cs; cs++) { + DEBUG_RL_C("DDR3 - Read Leveling - CS - ", (u32) cs, 1); + + for (ecc = 0; ecc <= (dram_info->ecc_ena); ecc++) { + /* ECC Support - Switch ECC Mux on ecc=1 */ + reg = reg_read(REG_DRAM_TRAINING_2_ADDR) & + ~(1 << REG_DRAM_TRAINING_2_ECC_MUX_OFFS); + reg |= (dram_info->ecc_ena * + ecc << REG_DRAM_TRAINING_2_ECC_MUX_OFFS); + reg_write(REG_DRAM_TRAINING_2_ADDR, reg); + + if (ecc) + DEBUG_RL_S("DDR3 - Read Leveling - ECC Mux Enabled\n"); + else + DEBUG_RL_S("DDR3 - Read Leveling - ECC Mux Disabled\n"); + + /* Set current sample delays */ + reg = reg_read(REG_READ_DATA_SAMPLE_DELAYS_ADDR); + reg &= ~(REG_READ_DATA_SAMPLE_DELAYS_MASK << + (REG_READ_DATA_SAMPLE_DELAYS_OFFS * cs)); + reg |= (dram_info->cl << + (REG_READ_DATA_SAMPLE_DELAYS_OFFS * cs)); + reg_write(REG_READ_DATA_SAMPLE_DELAYS_ADDR, reg); + + /* Set current Ready delay */ + reg = reg_read(REG_READ_DATA_READY_DELAYS_ADDR); + reg &= ~(REG_READ_DATA_READY_DELAYS_MASK << + (REG_READ_DATA_READY_DELAYS_OFFS * cs)); + if (!ratio_2to1) { + /* 1:1 mode */ + reg |= ((dram_info->cl + 1) << + (REG_READ_DATA_READY_DELAYS_OFFS * cs)); + } else { + /* 2:1 mode */ + reg |= ((dram_info->cl + 2) << + (REG_READ_DATA_READY_DELAYS_OFFS * cs)); + } + reg_write(REG_READ_DATA_READY_DELAYS_ADDR, reg); + + /* Read leveling Single CS[cs] */ +#ifdef RL_MODE + status = + ddr3_read_leveling_single_cs_rl_mode(cs, freq, + ratio_2to1, + ecc, + dram_info); + if (MV_OK != status) + return status; +#else + status = + ddr3_read_leveling_single_cs_window_mode(cs, freq, + ratio_2to1, + ecc, + dram_info) + if (MV_OK != status) + return status; +#endif + } + + /* Print results */ + DEBUG_RL_C("DDR3 - Read Leveling - Results for CS - ", (u32) cs, + 1); + + for (pup = 0; + pup < (dram_info->num_of_std_pups + dram_info->ecc_ena); + pup++) { + DEBUG_RL_S("DDR3 - Read Leveling - PUP: "); + DEBUG_RL_D((u32) pup, 1); + DEBUG_RL_S(", Phase: "); + DEBUG_RL_D((u32) dram_info->rl_val[cs][pup][P], 1); + DEBUG_RL_S(", Delay: "); + DEBUG_RL_D((u32) dram_info->rl_val[cs][pup][D], 2); + DEBUG_RL_S("\n"); + } + + DEBUG_RL_C("DDR3 - Read Leveling - Read Sample Delay: ", + dram_info->rd_smpl_dly, 2); + DEBUG_RL_C("DDR3 - Read Leveling - Read Ready Delay: ", + dram_info->rd_rdy_dly, 2); + + /* Configure PHY with average of 3 locked leveling settings */ + for (pup = 0; + pup < (dram_info->num_of_std_pups + dram_info->ecc_ena); + pup++) { + /* ECC support - bit 8 */ + pup_num = (pup == dram_info->num_of_std_pups) ? ECC_BIT : pup; + + /* For now, set last cnt result */ + phase = dram_info->rl_val[cs][pup][P]; + delay = dram_info->rl_val[cs][pup][D]; + ddr3_write_pup_reg(PUP_RL_MODE, cs, pup_num, phase, + delay); + } + } + + /* Reset PHY read FIFO */ + reg = reg_read(REG_DRAM_TRAINING_2_ADDR) | + (1 << REG_DRAM_TRAINING_2_FIFO_RST_OFFS); + /* 0x15B8 - Training SW 2 Register */ + reg_write(REG_DRAM_TRAINING_2_ADDR, reg); + + do { + reg = (reg_read(REG_DRAM_TRAINING_2_ADDR)) & + (1 << REG_DRAM_TRAINING_2_FIFO_RST_OFFS); + } while (reg); /* Wait for '0' */ + + /* ECC Support - Switch ECC Mux off ecc=0 */ + reg = reg_read(REG_DRAM_TRAINING_2_ADDR) & + ~(1 << REG_DRAM_TRAINING_2_ECC_MUX_OFFS); + reg_write(REG_DRAM_TRAINING_2_ADDR, reg); + +#ifdef RL_MODE + reg_write(REG_DRAM_TRAINING_ADDR, 0); /* 0x15B0 - Training Register */ +#endif + + /* Disable SW Read Leveling */ + reg = reg_read(REG_DRAM_TRAINING_2_ADDR) & + ~(1 << REG_DRAM_TRAINING_2_SW_OVRD_OFFS); + /* [0] = 0 - Disable SW override */ + reg = (reg | (0x1 << REG_DRAM_TRAINING_2_RL_MODE_OFFS)); + /* [3] = 1 - Disable RL MODE */ + /* 0x15B8 - Training SW 2 Register */ + reg_write(REG_DRAM_TRAINING_2_ADDR, reg); + + DEBUG_RL_S("DDR3 - Read Leveling - Finished RL procedure for all CS\n"); + return MV_OK; +} + +#ifdef RL_MODE +/* + * overrun() extracted from ddr3_read_leveling_single_cs_rl_mode(). + * This just got too much indented making it hard to read / edit. + */ +static void overrun(u32 cs, MV_DRAM_INFO *info, u32 pup, u32 locked_pups, + u32 *locked_sum, u32 ecc, int *first_octet_locked, + int *counter_in_progress, int final_delay, u32 delay, + u32 phase) +{ + /* If no OverRun */ + if (((~locked_pups >> pup) & 0x1) && (final_delay == 0)) { + int idx; + + idx = pup + ecc * ECC_BIT; + + /* PUP passed, start examining */ + if (info->rl_val[cs][idx][S] == RL_UNLOCK_STATE) { + /* Must be RL_UNLOCK_STATE */ + /* Match expected value ? - Update State Machine */ + if (info->rl_val[cs][idx][C] < RL_RETRY_COUNT) { + DEBUG_RL_FULL_C("DDR3 - Read Leveling - We have no overrun and a match on pup: ", + (u32)pup, 1); + info->rl_val[cs][idx][C]++; + + /* If pup got to last state - lock the delays */ + if (info->rl_val[cs][idx][C] == RL_RETRY_COUNT) { + info->rl_val[cs][idx][C] = 0; + info->rl_val[cs][idx][DS] = delay; + info->rl_val[cs][idx][PS] = phase; + + /* Go to Final State */ + info->rl_val[cs][idx][S] = RL_FINAL_STATE; + *locked_sum = *locked_sum + 1; + DEBUG_RL_FULL_C("DDR3 - Read Leveling - We have locked pup: ", + (u32)pup, 1); + + /* + * If first lock - need to lock delays + */ + if (*first_octet_locked == 0) { + DEBUG_RL_FULL_C("DDR3 - Read Leveling - We got first lock on pup: ", + (u32)pup, 1); + *first_octet_locked = 1; + } + + /* + * If pup is in not in final state but + * there was match - dont increment + * counter + */ + } else { + *counter_in_progress = 1; + } + } + } + } +} + +/* + * Name: ddr3_read_leveling_single_cs_rl_mode + * Desc: Execute Read leveling for single Chip select + * Args: cs - current chip select + * freq - current sequence frequency + * ecc - ecc iteration indication + * dram_info - main struct + * Notes: + * Returns: MV_OK if success, MV_FAIL if fail. + */ +static int ddr3_read_leveling_single_cs_rl_mode(u32 cs, u32 freq, + int ratio_2to1, u32 ecc, + MV_DRAM_INFO *dram_info) +{ + u32 reg, delay, phase, pup, rd_sample_delay, add, locked_pups, + repeat_max_cnt, sdram_offset, locked_sum; + u32 phase_min, ui_max_delay; + int all_locked, first_octet_locked, counter_in_progress; + int final_delay = 0; + + DEBUG_RL_FULL_C("DDR3 - Read Leveling - Single CS - ", (u32) cs, 1); + + /* Init values */ + phase = 0; + delay = 0; + rd_sample_delay = dram_info->cl; + all_locked = 0; + first_octet_locked = 0; + repeat_max_cnt = 0; + locked_sum = 0; + + for (pup = 0; pup < (dram_info->num_of_std_pups * (1 - ecc) + ecc); + pup++) + dram_info->rl_val[cs][pup + ecc * ECC_BIT][S] = 0; + + /* Main loop */ + while (!all_locked) { + counter_in_progress = 0; + + DEBUG_RL_FULL_S("DDR3 - Read Leveling - RdSmplDly = "); + DEBUG_RL_FULL_D(rd_sample_delay, 2); + DEBUG_RL_FULL_S(", RdRdyDly = "); + DEBUG_RL_FULL_D(dram_info->rd_rdy_dly, 2); + DEBUG_RL_FULL_S(", Phase = "); + DEBUG_RL_FULL_D(phase, 1); + DEBUG_RL_FULL_S(", Delay = "); + DEBUG_RL_FULL_D(delay, 2); + DEBUG_RL_FULL_S("\n"); + + /* + * Broadcast to all PUPs current RL delays: DQS phase, + * leveling delay + */ + ddr3_write_pup_reg(PUP_RL_MODE, cs, PUP_BC, phase, delay); + + /* Reset PHY read FIFO */ + reg = reg_read(REG_DRAM_TRAINING_2_ADDR) | + (1 << REG_DRAM_TRAINING_2_FIFO_RST_OFFS); + /* 0x15B8 - Training SW 2 Register */ + reg_write(REG_DRAM_TRAINING_2_ADDR, reg); + + do { + reg = (reg_read(REG_DRAM_TRAINING_2_ADDR)) & + (1 << REG_DRAM_TRAINING_2_FIFO_RST_OFFS); + } while (reg); /* Wait for '0' */ + + /* Read pattern from SDRAM */ + sdram_offset = cs * (SDRAM_CS_SIZE + 1) + SDRAM_RL_OFFS; + locked_pups = 0; + if (MV_OK != + ddr3_sdram_compare(dram_info, 0xFF, &locked_pups, + rl_pattern, LEN_STD_PATTERN, + sdram_offset, 0, 0, NULL, 0)) + return MV_DDR3_TRAINING_ERR_RD_LVL_RL_PATTERN; + + /* Octet evaluation */ + /* pup_num = Q or 1 for ECC */ + for (pup = 0; pup < (dram_info->num_of_std_pups * (1 - ecc) + ecc); pup++) { + /* Check Overrun */ + if (!((reg_read(REG_DRAM_TRAINING_2_ADDR) >> + (REG_DRAM_TRAINING_2_OVERRUN_OFFS + pup)) & 0x1)) { + overrun(cs, dram_info, pup, locked_pups, + &locked_sum, ecc, &first_octet_locked, + &counter_in_progress, final_delay, + delay, phase); + } else { + DEBUG_RL_FULL_C("DDR3 - Read Leveling - We got overrun on pup: ", + (u32)pup, 1); + } + } + + if (locked_sum == (dram_info->num_of_std_pups * + (1 - ecc) + ecc)) { + all_locked = 1; + DEBUG_RL_FULL_S("DDR3 - Read Leveling - Single Cs - All pups locked\n"); + } + + /* + * This is a fix for unstable condition where pups are + * toggling between match and no match + */ + /* + * If some of the pups is >1 <3, check if we did it too + * many times + */ + if (counter_in_progress == 1) { + /* Notify at least one Counter is >=1 and < 3 */ + if (repeat_max_cnt < RL_RETRY_COUNT) { + repeat_max_cnt++; + counter_in_progress = 1; + DEBUG_RL_FULL_S("DDR3 - Read Leveling - Counter is >=1 and <3\n"); + DEBUG_RL_FULL_S("DDR3 - Read Leveling - So we will not increment the delay to see if locked again\n"); + } else { + DEBUG_RL_FULL_S("DDR3 - Read Leveling - repeat_max_cnt reached max so now we will increment the delay\n"); + counter_in_progress = 0; + } + } + + /* + * Check some of the pups are in the middle of state machine + * and don't increment the delays + */ + if (!counter_in_progress && !all_locked) { + int idx; + + idx = pup + ecc * ECC_BIT; + + repeat_max_cnt = 0; + /* if 1:1 mode */ + if ((!ratio_2to1) && ((phase == 0) || (phase == 4))) + ui_max_delay = MAX_DELAY_INV; + else + ui_max_delay = MAX_DELAY; + + /* Increment Delay */ + if (delay < ui_max_delay) { + delay++; + /* + * Mark the last delay/pahse place for + * window final place + */ + if (delay == ui_max_delay) { + if ((!ratio_2to1 && phase == + MAX_PHASE_RL_L_1TO1) + || (ratio_2to1 && phase == + MAX_PHASE_RL_L_2TO1)) + final_delay = 1; + } + } else { + /* Phase+CL Incrementation */ + delay = 0; + + if (!ratio_2to1) { + /* 1:1 mode */ + if (first_octet_locked) { + /* some Pup was Locked */ + if (phase < MAX_PHASE_RL_L_1TO1) { + if (phase == 1) { + phase = 4; + } else { + phase++; + delay = MIN_DELAY_PHASE_1_LIMIT; + } + } else { + DEBUG_RL_FULL_S("DDR3 - Read Leveling - ERROR - NOT all PUPs Locked\n"); + DEBUG_RL_S("1)DDR3 - Read Leveling - ERROR - NOT all PUPs Locked n"); + return MV_DDR3_TRAINING_ERR_RD_LVL_RL_PUP_UNLOCK; + } + } else { + /* NO Pup was Locked */ + if (phase < MAX_PHASE_RL_UL_1TO1) { + phase++; + delay = + MIN_DELAY_PHASE_1_LIMIT; + } else { + phase = 0; + } + } + } else { + /* 2:1 mode */ + if (first_octet_locked) { + /* some Pup was Locked */ + if (phase < MAX_PHASE_RL_L_2TO1) { + phase++; + } else { + DEBUG_RL_FULL_S("DDR3 - Read Leveling - ERROR - NOT all PUPs Locked\n"); + DEBUG_RL_S("2)DDR3 - Read Leveling - ERROR - NOT all PUPs Locked\n"); + for (pup = 0; pup < (dram_info->num_of_std_pups * (1 - ecc) + ecc); pup++) { + /* pup_num = Q or 1 for ECC */ + if (dram_info->rl_val[cs][idx][S] + == 0) { + DEBUG_RL_C("Failed byte is = ", + pup, 1); + } + } + return MV_DDR3_TRAINING_ERR_RD_LVL_RL_PUP_UNLOCK; + } + } else { + /* No Pup was Locked */ + if (phase < MAX_PHASE_RL_UL_2TO1) + phase++; + else + phase = 0; + } + } + + /* + * If we finished a full Phases cycle (so now + * phase = 0, need to increment rd_sample_dly + */ + if (phase == 0 && first_octet_locked == 0) { + rd_sample_delay++; + if (rd_sample_delay == 0x10) { + DEBUG_RL_FULL_S("DDR3 - Read Leveling - ERROR - NOT all PUPs Locked\n"); + DEBUG_RL_S("3)DDR3 - Read Leveling - ERROR - NOT all PUPs Locked\n"); + for (pup = 0; pup < (dram_info->num_of_std_pups * (1 - ecc) + ecc); pup++) { + /* pup_num = Q or 1 for ECC */ + if (dram_info-> + rl_val[cs][idx][S] == 0) { + DEBUG_RL_C("Failed byte is = ", + pup, 1); + } + } + return MV_DDR3_TRAINING_ERR_RD_LVL_PUP_UNLOCK; + } + + /* Set current rd_sample_delay */ + reg = reg_read(REG_READ_DATA_SAMPLE_DELAYS_ADDR); + reg &= ~(REG_READ_DATA_SAMPLE_DELAYS_MASK + << (REG_READ_DATA_SAMPLE_DELAYS_OFFS + * cs)); + reg |= (rd_sample_delay << + (REG_READ_DATA_SAMPLE_DELAYS_OFFS * + cs)); + reg_write(REG_READ_DATA_SAMPLE_DELAYS_ADDR, + reg); + } + + /* + * Set current rdReadyDelay according to the + * hash table (Need to do this in every phase + * change) + */ + if (!ratio_2to1) { + /* 1:1 mode */ + add = reg_read(REG_TRAINING_DEBUG_2_ADDR); + switch (phase) { + case 0: + add = (add >> + REG_TRAINING_DEBUG_2_OFFS); + break; + case 1: + add = (add >> + (REG_TRAINING_DEBUG_2_OFFS + + 3)); + break; + case 4: + add = (add >> + (REG_TRAINING_DEBUG_2_OFFS + + 6)); + break; + case 5: + add = (add >> + (REG_TRAINING_DEBUG_2_OFFS + + 9)); + break; + } + add &= REG_TRAINING_DEBUG_2_MASK; + } else { + /* 2:1 mode */ + add = reg_read(REG_TRAINING_DEBUG_3_ADDR); + add = (add >> + (phase * + REG_TRAINING_DEBUG_3_OFFS)); + add &= REG_TRAINING_DEBUG_3_MASK; + } + + reg = reg_read(REG_READ_DATA_READY_DELAYS_ADDR); + reg &= ~(REG_READ_DATA_READY_DELAYS_MASK << + (REG_READ_DATA_READY_DELAYS_OFFS * cs)); + reg |= ((rd_sample_delay + add) << + (REG_READ_DATA_READY_DELAYS_OFFS * cs)); + reg_write(REG_READ_DATA_READY_DELAYS_ADDR, reg); + dram_info->rd_smpl_dly = rd_sample_delay; + dram_info->rd_rdy_dly = rd_sample_delay + add; + } + + /* Reset counters for pups with states<RD_STATE_COUNT */ + for (pup = 0; pup < + (dram_info->num_of_std_pups * (1 - ecc) + ecc); + pup++) { + if (dram_info->rl_val[cs][idx][C] < RL_RETRY_COUNT) + dram_info->rl_val[cs][idx][C] = 0; + } + } + } + + phase_min = 10; + + for (pup = 0; pup < (dram_info->num_of_std_pups); pup++) { + if (dram_info->rl_val[cs][pup][PS] < phase_min) + phase_min = dram_info->rl_val[cs][pup][PS]; + } + + /* + * Set current rdReadyDelay according to the hash table (Need to + * do this in every phase change) + */ + if (!ratio_2to1) { + /* 1:1 mode */ + add = reg_read(REG_TRAINING_DEBUG_2_ADDR); + switch (phase_min) { + case 0: + add = (add >> REG_TRAINING_DEBUG_2_OFFS); + break; + case 1: + add = (add >> (REG_TRAINING_DEBUG_2_OFFS + 3)); + break; + case 4: + add = (add >> (REG_TRAINING_DEBUG_2_OFFS + 6)); + break; + case 5: + add = (add >> (REG_TRAINING_DEBUG_2_OFFS + 9)); + break; + } + add &= REG_TRAINING_DEBUG_2_MASK; + } else { + /* 2:1 mode */ + add = reg_read(REG_TRAINING_DEBUG_3_ADDR); + add = (add >> (phase_min * REG_TRAINING_DEBUG_3_OFFS)); + add &= REG_TRAINING_DEBUG_3_MASK; + } + + reg = reg_read(REG_READ_DATA_READY_DELAYS_ADDR); + reg &= ~(REG_READ_DATA_READY_DELAYS_MASK << + (REG_READ_DATA_READY_DELAYS_OFFS * cs)); + reg |= ((rd_sample_delay + add) << (REG_READ_DATA_READY_DELAYS_OFFS * cs)); + reg_write(REG_READ_DATA_READY_DELAYS_ADDR, reg); + dram_info->rd_rdy_dly = rd_sample_delay + add; + + for (cs = 0; cs < dram_info->num_cs; cs++) { + for (pup = 0; pup < dram_info->num_of_total_pups; pup++) { + reg = ddr3_read_pup_reg(PUP_RL_MODE + 0x1, cs, pup); + dram_info->rl_val[cs][pup][DQS] = (reg & 0x3F); + } + } + + return MV_OK; +} + +#else + +/* + * Name: ddr3_read_leveling_single_cs_window_mode + * Desc: Execute Read leveling for single Chip select + * Args: cs - current chip select + * freq - current sequence frequency + * ecc - ecc iteration indication + * dram_info - main struct + * Notes: + * Returns: MV_OK if success, MV_FAIL if fail. + */ +static int ddr3_read_leveling_single_cs_window_mode(u32 cs, u32 freq, + int ratio_2to1, u32 ecc, + MV_DRAM_INFO *dram_info) +{ + u32 reg, delay, phase, sum, pup, rd_sample_delay, add, locked_pups, + repeat_max_cnt, sdram_offset, final_sum, locked_sum; + u32 delay_s, delay_e, tmp, phase_min, ui_max_delay; + int all_locked, first_octet_locked, counter_in_progress; + int final_delay = 0; + + DEBUG_RL_FULL_C("DDR3 - Read Leveling - Single CS - ", (u32) cs, 1); + + /* Init values */ + phase = 0; + delay = 0; + rd_sample_delay = dram_info->cl; + all_locked = 0; + first_octet_locked = 0; + repeat_max_cnt = 0; + sum = 0; + final_sum = 0; + locked_sum = 0; + + for (pup = 0; pup < (dram_info->num_of_std_pups * (1 - ecc) + ecc); + pup++) + dram_info->rl_val[cs][pup + ecc * ECC_BIT][S] = 0; + + /* Main loop */ + while (!all_locked) { + counter_in_progress = 0; + + DEBUG_RL_FULL_S("DDR3 - Read Leveling - RdSmplDly = "); + DEBUG_RL_FULL_D(rd_sample_delay, 2); + DEBUG_RL_FULL_S(", RdRdyDly = "); + DEBUG_RL_FULL_D(dram_info->rd_rdy_dly, 2); + DEBUG_RL_FULL_S(", Phase = "); + DEBUG_RL_FULL_D(phase, 1); + DEBUG_RL_FULL_S(", Delay = "); + DEBUG_RL_FULL_D(delay, 2); + DEBUG_RL_FULL_S("\n"); + + /* + * Broadcast to all PUPs current RL delays: DQS phase,leveling + * delay + */ + ddr3_write_pup_reg(PUP_RL_MODE, cs, PUP_BC, phase, delay); + + /* Reset PHY read FIFO */ + reg = reg_read(REG_DRAM_TRAINING_2_ADDR) | + (1 << REG_DRAM_TRAINING_2_FIFO_RST_OFFS); + /* 0x15B8 - Training SW 2 Register */ + reg_write(REG_DRAM_TRAINING_2_ADDR, reg); + + do { + reg = (reg_read(REG_DRAM_TRAINING_2_ADDR)) & + (1 << REG_DRAM_TRAINING_2_FIFO_RST_OFFS); + } while (reg); /* Wait for '0' */ + + /* Read pattern from SDRAM */ + sdram_offset = cs * (SDRAM_CS_SIZE + 1) + SDRAM_RL_OFFS; + locked_pups = 0; + if (MV_OK != + ddr3_sdram_compare(dram_info, 0xFF, &locked_pups, + rl_pattern, LEN_STD_PATTERN, + sdram_offset, 0, 0, NULL, 0)) + return MV_DDR3_TRAINING_ERR_RD_LVL_WIN_PATTERN; + + /* Octet evaluation */ + for (pup = 0; pup < (dram_info->num_of_std_pups * + (1 - ecc) + ecc); pup++) { + /* pup_num = Q or 1 for ECC */ + int idx; + + idx = pup + ecc * ECC_BIT; + + /* Check Overrun */ + if (!((reg_read(REG_DRAM_TRAINING_2_ADDR) >> + (REG_DRAM_TRAINING_2_OVERRUN_OFFS + + pup)) & 0x1)) { + /* If no OverRun */ + + /* Inside the window */ + if (dram_info->rl_val[cs][idx][S] == RL_WINDOW_STATE) { + /* + * Match expected value ? - Update + * State Machine + */ + if (((~locked_pups >> pup) & 0x1) + && (final_delay == 0)) { + /* Match - Still inside the Window */ + DEBUG_RL_FULL_C("DDR3 - Read Leveling - We got another match inside the window for pup: ", + (u32)pup, 1); + + } else { + /* We got fail -> this is the end of the window */ + dram_info->rl_val[cs][idx][DE] = delay; + dram_info->rl_val[cs][idx][PE] = phase; + /* Go to Final State */ + dram_info->rl_val[cs][idx][S]++; + final_sum++; + DEBUG_RL_FULL_C("DDR3 - Read Leveling - We finished the window for pup: ", + (u32)pup, 1); + } + + /* Before the start of the window */ + } else if (dram_info->rl_val[cs][idx][S] == + RL_UNLOCK_STATE) { + /* Must be RL_UNLOCK_STATE */ + /* + * Match expected value ? - Update + * State Machine + */ + if (dram_info->rl_val[cs][idx][C] < + RL_RETRY_COUNT) { + if (((~locked_pups >> pup) & 0x1)) { + /* Match */ + DEBUG_RL_FULL_C("DDR3 - Read Leveling - We have no overrun and a match on pup: ", + (u32)pup, 1); + dram_info->rl_val[cs][idx][C]++; + + /* If pup got to last state - lock the delays */ + if (dram_info->rl_val[cs][idx][C] == + RL_RETRY_COUNT) { + dram_info->rl_val[cs][idx][C] = 0; + dram_info->rl_val[cs][idx][DS] = + delay; + dram_info->rl_val[cs][idx][PS] = + phase; + dram_info->rl_val[cs][idx][S]++; /* Go to Window State */ + locked_sum++; + /* Will count the pups that got locked */ + + /* IF First lock - need to lock delays */ + if (first_octet_locked == 0) { + DEBUG_RL_FULL_C("DDR3 - Read Leveling - We got first lock on pup: ", + (u32)pup, 1); + first_octet_locked + = + 1; + } + } + + /* if pup is in not in final state but there was match - dont increment counter */ + else { + counter_in_progress + = 1; + } + } + } + } + } else { + DEBUG_RL_FULL_C("DDR3 - Read Leveling - We got overrun on pup: ", + (u32)pup, 1); + counter_in_progress = 1; + } + } + + if (final_sum == (dram_info->num_of_std_pups * (1 - ecc) + ecc)) { + all_locked = 1; + DEBUG_RL_FULL_S("DDR3 - Read Leveling - Single Cs - All pups locked\n"); + } + + /* + * This is a fix for unstable condition where pups are + * toggling between match and no match + */ + /* + * If some of the pups is >1 <3, check if we did it too many + * times + */ + if (counter_in_progress == 1) { + if (repeat_max_cnt < RL_RETRY_COUNT) { + /* Notify at least one Counter is >=1 and < 3 */ + repeat_max_cnt++; + counter_in_progress = 1; + DEBUG_RL_FULL_S("DDR3 - Read Leveling - Counter is >=1 and <3\n"); + DEBUG_RL_FULL_S("DDR3 - Read Leveling - So we will not increment the delay to see if locked again\n"); + } else { + DEBUG_RL_FULL_S("DDR3 - Read Leveling - repeat_max_cnt reached max so now we will increment the delay\n"); + counter_in_progress = 0; + } + } + + /* + * Check some of the pups are in the middle of state machine + * and don't increment the delays + */ + if (!counter_in_progress && !all_locked) { + repeat_max_cnt = 0; + if (!ratio_2to1) + ui_max_delay = MAX_DELAY_INV; + else + ui_max_delay = MAX_DELAY; + + /* Increment Delay */ + if (delay < ui_max_delay) { + /* Delay Incrementation */ + delay++; + if (delay == ui_max_delay) { + /* + * Mark the last delay/pahse place + * for window final place + */ + if ((!ratio_2to1 + && phase == MAX_PHASE_RL_L_1TO1) + || (ratio_2to1 + && phase == + MAX_PHASE_RL_L_2TO1)) + final_delay = 1; + } + } else { + /* Phase+CL Incrementation */ + delay = 0; + if (!ratio_2to1) { + /* 1:1 mode */ + if (first_octet_locked) { + /* some pupet was Locked */ + if (phase < MAX_PHASE_RL_L_1TO1) { +#ifdef RL_WINDOW_WA + if (phase == 0) +#else + if (phase == 1) +#endif + phase = 4; + else + phase++; + } else { + DEBUG_RL_FULL_S("DDR3 - Read Leveling - ERROR - NOT all PUPs Locked\n"); + return MV_DDR3_TRAINING_ERR_RD_LVL_WIN_PUP_UNLOCK; + } + } else { + /* No Pup was Locked */ + if (phase < MAX_PHASE_RL_UL_1TO1) { +#ifdef RL_WINDOW_WA + if (phase == 0) + phase = 4; +#else + phase++; +#endif + } else + phase = 0; + } + } else { + /* 2:1 mode */ + if (first_octet_locked) { + /* Some Pup was Locked */ + if (phase < MAX_PHASE_RL_L_2TO1) { + phase++; + } else { + DEBUG_RL_FULL_S("DDR3 - Read Leveling - ERROR - NOT all PUPs Locked\n"); + return MV_DDR3_TRAINING_ERR_RD_LVL_WIN_PUP_UNLOCK; + } + } else { + /* No Pup was Locked */ + if (phase < MAX_PHASE_RL_UL_2TO1) + phase++; + else + phase = 0; + } + } + + /* + * If we finished a full Phases cycle (so + * now phase = 0, need to increment + * rd_sample_dly + */ + if (phase == 0 && first_octet_locked == 0) { + rd_sample_delay++; + + /* Set current rd_sample_delay */ + reg = reg_read(REG_READ_DATA_SAMPLE_DELAYS_ADDR); + reg &= ~(REG_READ_DATA_SAMPLE_DELAYS_MASK << + (REG_READ_DATA_SAMPLE_DELAYS_OFFS + * cs)); + reg |= (rd_sample_delay << + (REG_READ_DATA_SAMPLE_DELAYS_OFFS * + cs)); + reg_write(REG_READ_DATA_SAMPLE_DELAYS_ADDR, + reg); + } + + /* + * Set current rdReadyDelay according to the + * hash table (Need to do this in every phase + * change) + */ + if (!ratio_2to1) { + /* 1:1 mode */ + add = reg_read(REG_TRAINING_DEBUG_2_ADDR); + switch (phase) { + case 0: + add = add >> + REG_TRAINING_DEBUG_2_OFFS; + break; + case 1: + add = add >> + (REG_TRAINING_DEBUG_2_OFFS + + 3); + break; + case 4: + add = add >> + (REG_TRAINING_DEBUG_2_OFFS + + 6); + break; + case 5: + add = add >> + (REG_TRAINING_DEBUG_2_OFFS + + 9); + break; + } + } else { + /* 2:1 mode */ + add = reg_read(REG_TRAINING_DEBUG_3_ADDR); + add = (add >> phase * + REG_TRAINING_DEBUG_3_OFFS); + } + add &= REG_TRAINING_DEBUG_2_MASK; + reg = reg_read(REG_READ_DATA_READY_DELAYS_ADDR); + reg &= ~(REG_READ_DATA_READY_DELAYS_MASK << + (REG_READ_DATA_READY_DELAYS_OFFS * cs)); + reg |= ((rd_sample_delay + add) << + (REG_READ_DATA_READY_DELAYS_OFFS * cs)); + reg_write(REG_READ_DATA_READY_DELAYS_ADDR, reg); + dram_info->rd_smpl_dly = rd_sample_delay; + dram_info->rd_rdy_dly = rd_sample_delay + add; + } + + /* Reset counters for pups with states<RD_STATE_COUNT */ + for (pup = 0; + pup < + (dram_info->num_of_std_pups * (1 - ecc) + ecc); + pup++) { + if (dram_info->rl_val[cs][idx][C] < RL_RETRY_COUNT) + dram_info->rl_val[cs][idx][C] = 0; + } + } + } + + phase_min = 10; + + for (pup = 0; pup < (dram_info->num_of_std_pups); pup++) { + DEBUG_RL_S("DDR3 - Read Leveling - Window info - PUP: "); + DEBUG_RL_D((u32) pup, 1); + DEBUG_RL_S(", PS: "); + DEBUG_RL_D((u32) dram_info->rl_val[cs][pup][PS], 1); + DEBUG_RL_S(", DS: "); + DEBUG_RL_D((u32) dram_info->rl_val[cs][pup][DS], 2); + DEBUG_RL_S(", PE: "); + DEBUG_RL_D((u32) dram_info->rl_val[cs][pup][PE], 1); + DEBUG_RL_S(", DE: "); + DEBUG_RL_D((u32) dram_info->rl_val[cs][pup][DE], 2); + DEBUG_RL_S("\n"); + } + + /* Find center of the window procedure */ + for (pup = 0; pup < (dram_info->num_of_std_pups * (1 - ecc) + ecc); + pup++) { +#ifdef RL_WINDOW_WA + if (!ratio_2to1) { /* 1:1 mode */ + if (dram_info->rl_val[cs][idx][PS] == 4) + dram_info->rl_val[cs][idx][PS] = 1; + if (dram_info->rl_val[cs][idx][PE] == 4) + dram_info->rl_val[cs][idx][PE] = 1; + + delay_s = dram_info->rl_val[cs][idx][PS] * + MAX_DELAY_INV + dram_info->rl_val[cs][idx][DS]; + delay_e = dram_info->rl_val[cs][idx][PE] * + MAX_DELAY_INV + dram_info->rl_val[cs][idx][DE]; + + tmp = (delay_e - delay_s) / 2 + delay_s; + phase = tmp / MAX_DELAY_INV; + if (phase == 1) /* 1:1 mode */ + phase = 4; + + if (phase < phase_min) /* for the read ready delay */ + phase_min = phase; + + dram_info->rl_val[cs][idx][P] = phase; + dram_info->rl_val[cs][idx][D] = tmp % MAX_DELAY_INV; + + } else { + delay_s = dram_info->rl_val[cs][idx][PS] * + MAX_DELAY + dram_info->rl_val[cs][idx][DS]; + delay_e = dram_info->rl_val[cs][idx][PE] * + MAX_DELAY + dram_info->rl_val[cs][idx][DE]; + + tmp = (delay_e - delay_s) / 2 + delay_s; + phase = tmp / MAX_DELAY; + + if (phase < phase_min) /* for the read ready delay */ + phase_min = phase; + + dram_info->rl_val[cs][idx][P] = phase; + dram_info->rl_val[cs][idx][D] = tmp % MAX_DELAY; + } +#else + if (!ratio_2to1) { /* 1:1 mode */ + if (dram_info->rl_val[cs][idx][PS] > 1) + dram_info->rl_val[cs][idx][PS] -= 2; + if (dram_info->rl_val[cs][idx][PE] > 1) + dram_info->rl_val[cs][idx][PE] -= 2; + } + + delay_s = dram_info->rl_val[cs][idx][PS] * MAX_DELAY + + dram_info->rl_val[cs][idx][DS]; + delay_e = dram_info->rl_val[cs][idx][PE] * MAX_DELAY + + dram_info->rl_val[cs][idx][DE]; + + tmp = (delay_e - delay_s) / 2 + delay_s; + phase = tmp / MAX_DELAY; + if (!ratio_2to1 && phase > 1) /* 1:1 mode */ + phase += 2; + + if (phase < phase_min) /* for the read ready delay */ + phase_min = phase; + + dram_info->rl_val[cs][idx][P] = phase; + dram_info->rl_val[cs][idx][D] = tmp % MAX_DELAY; +#endif + } + + /* Set current rdReadyDelay according to the hash table (Need to do this in every phase change) */ + if (!ratio_2to1) { /* 1:1 mode */ + add = reg_read(REG_TRAINING_DEBUG_2_ADDR); + switch (phase_min) { + case 0: + add = (add >> REG_TRAINING_DEBUG_2_OFFS); + break; + case 1: + add = (add >> (REG_TRAINING_DEBUG_2_OFFS + 3)); + break; + case 4: + add = (add >> (REG_TRAINING_DEBUG_2_OFFS + 6)); + break; + case 5: + add = (add >> (REG_TRAINING_DEBUG_2_OFFS + 9)); + break; + } + } else { /* 2:1 mode */ + add = reg_read(REG_TRAINING_DEBUG_3_ADDR); + add = (add >> phase_min * REG_TRAINING_DEBUG_3_OFFS); + } + + add &= REG_TRAINING_DEBUG_2_MASK; + reg = reg_read(REG_READ_DATA_READY_DELAYS_ADDR); + reg &= + ~(REG_READ_DATA_READY_DELAYS_MASK << + (REG_READ_DATA_READY_DELAYS_OFFS * cs)); + reg |= + ((rd_sample_delay + add) << (REG_READ_DATA_READY_DELAYS_OFFS * cs)); + reg_write(REG_READ_DATA_READY_DELAYS_ADDR, reg); + dram_info->rd_rdy_dly = rd_sample_delay + add; + + for (cs = 0; cs < dram_info->num_cs; cs++) { + for (pup = 0; pup < dram_info->num_of_total_pups; pup++) { + reg = ddr3_read_pup_reg(PUP_RL_MODE + 0x1, cs, pup); + dram_info->rl_val[cs][pup][DQS] = (reg & 0x3F); + } + } + + return MV_OK; +} +#endif diff --git a/drivers/ddr/mvebu/ddr3_sdram.c b/drivers/ddr/mvebu/ddr3_sdram.c new file mode 100644 index 0000000..50c1bf8 --- /dev/null +++ b/drivers/ddr/mvebu/ddr3_sdram.c @@ -0,0 +1,669 @@ +/* + * Copyright (C) Marvell International Ltd. and its affiliates + * + * SPDX-License-Identifier: GPL-2.0 + */ + +#include <common.h> +#include <i2c.h> +#include <spl.h> +#include <asm/io.h> +#include <asm/arch/cpu.h> +#include <asm/arch/soc.h> + +#include "ddr3_hw_training.h" +#include "xor.h" +#include "xor_regs.h" + +static void ddr3_flush_l1_line(u32 line); + +extern u32 pbs_pattern[2][LEN_16BIT_PBS_PATTERN]; +extern u32 pbs_pattern_32b[2][LEN_PBS_PATTERN]; +#if defined(MV88F78X60) +extern u32 pbs_pattern_64b[2][LEN_PBS_PATTERN]; +#endif +extern u32 pbs_dq_mapping[PUP_NUM_64BIT][DQ_NUM]; + +#if defined(MV88F78X60) || defined(MV88F672X) +/* PBS locked dq (per pup) */ +u32 pbs_locked_dq[MAX_PUP_NUM][DQ_NUM] = { { 0 } }; +u32 pbs_locked_dm[MAX_PUP_NUM] = { 0 }; +u32 pbs_locked_value[MAX_PUP_NUM][DQ_NUM] = { { 0 } }; + +int per_bit_data[MAX_PUP_NUM][DQ_NUM]; +#endif + +static u32 sdram_data[LEN_KILLER_PATTERN] __aligned(32) = { 0 }; + +static struct crc_dma_desc dma_desc __aligned(32) = { 0 }; + +#define XOR_TIMEOUT 0x8000000 + +struct xor_channel_t { + struct crc_dma_desc *desc; + unsigned long desc_phys_addr; +}; + +#define XOR_CAUSE_DONE_MASK(chan) ((0x1 | 0x2) << (chan * 16)) + +void xor_waiton_eng(int chan) +{ + int timeout; + + timeout = 0; + while (!(reg_read(XOR_CAUSE_REG(XOR_UNIT(chan))) & + XOR_CAUSE_DONE_MASK(XOR_CHAN(chan)))) { + if (timeout > XOR_TIMEOUT) + goto timeout; + + timeout++; + } + + timeout = 0; + while (mv_xor_state_get(chan) != MV_IDLE) { + if (timeout > XOR_TIMEOUT) + goto timeout; + + timeout++; + } + + /* Clear int */ + reg_write(XOR_CAUSE_REG(XOR_UNIT(chan)), + ~(XOR_CAUSE_DONE_MASK(XOR_CHAN(chan)))); + +timeout: + return; +} + +static int special_compare_pattern(u32 uj) +{ + if ((uj == 30) || (uj == 31) || (uj == 61) || (uj == 62) || + (uj == 93) || (uj == 94) || (uj == 126) || (uj == 127)) + return 1; + + return 0; +} + +/* + * Compare code extracted as its used by multiple functions. This + * reduces code-size and makes it easier to maintain it. Additionally + * the code is not indented that much and therefore easier to read. + */ +static void compare_pattern_v1(u32 uj, u32 *pup, u32 *pattern, + u32 pup_groups, int debug_dqs) +{ + u32 val; + u32 uk; + u32 var1; + u32 var2; + __maybe_unused u32 dq; + + if (((sdram_data[uj]) != (pattern[uj])) && (*pup != 0xFF)) { + for (uk = 0; uk < PUP_NUM_32BIT; uk++) { + val = CMP_BYTE_SHIFT * uk; + var1 = ((sdram_data[uj] >> val) & CMP_BYTE_MASK); + var2 = ((pattern[uj] >> val) & CMP_BYTE_MASK); + + if (var1 != var2) { + *pup |= (1 << (uk + (PUP_NUM_32BIT * + (uj % pup_groups)))); + +#ifdef MV_DEBUG_DQS + if (!debug_dqs) + continue; + + for (dq = 0; dq < DQ_NUM; dq++) { + val = uk + (PUP_NUM_32BIT * + (uj % pup_groups)); + if (((var1 >> dq) & 0x1) != + ((var2 >> dq) & 0x1)) + per_bit_data[val][dq] = 1; + else + per_bit_data[val][dq] = 0; + } +#endif + } + } + } +} + +static void compare_pattern_v2(u32 uj, u32 *pup, u32 *pattern) +{ + u32 val; + u32 uk; + u32 var1; + u32 var2; + + if (((sdram_data[uj]) != (pattern[uj])) && (*pup != 0x3)) { + /* Found error */ + for (uk = 0; uk < PUP_NUM_32BIT; uk++) { + val = CMP_BYTE_SHIFT * uk; + var1 = (sdram_data[uj] >> val) & CMP_BYTE_MASK; + var2 = (pattern[uj] >> val) & CMP_BYTE_MASK; + if (var1 != var2) + *pup |= (1 << (uk % PUP_NUM_16BIT)); + } + } +} + +/* + * Name: ddr3_sdram_compare + * Desc: Execute compare per PUP + * Args: unlock_pup Bit array of the unlock pups + * new_locked_pup Output bit array of the pups with failed compare + * pattern Pattern to compare + * pattern_len Length of pattern (in bytes) + * sdram_offset offset address to the SDRAM + * write write to the SDRAM before read + * mask compare pattern with mask; + * mask_pattern Mask to compare pattern + * + * Notes: + * Returns: MV_OK if success, other error code if fail. + */ +int ddr3_sdram_compare(MV_DRAM_INFO *dram_info, u32 unlock_pup, + u32 *new_locked_pup, u32 *pattern, + u32 pattern_len, u32 sdram_offset, int write, + int mask, u32 *mask_pattern, + int special_compare) +{ + u32 uj; + __maybe_unused u32 pup_groups; + __maybe_unused u32 dq; + +#if !defined(MV88F67XX) + if (dram_info->num_of_std_pups == PUP_NUM_64BIT) + pup_groups = 2; + else + pup_groups = 1; +#endif + + ddr3_reset_phy_read_fifo(); + + /* Check if need to write to sdram before read */ + if (write == 1) + ddr3_dram_sram_burst((u32)pattern, sdram_offset, pattern_len); + + ddr3_dram_sram_burst(sdram_offset, (u32)sdram_data, pattern_len); + + /* Compare read result to write */ + for (uj = 0; uj < pattern_len; uj++) { + if (special_compare && special_compare_pattern(uj)) + continue; + +#if defined(MV88F78X60) || defined(MV88F672X) + compare_pattern_v1(uj, new_locked_pup, pattern, pup_groups, 1); +#elif defined(MV88F67XX) + compare_pattern_v2(uj, new_locked_pup, pattern); +#endif + } + + return MV_OK; +} + +#if defined(MV88F78X60) || defined(MV88F672X) +/* + * Name: ddr3_sdram_dm_compare + * Desc: Execute compare per PUP + * Args: unlock_pup Bit array of the unlock pups + * new_locked_pup Output bit array of the pups with failed compare + * pattern Pattern to compare + * pattern_len Length of pattern (in bytes) + * sdram_offset offset address to the SDRAM + * write write to the SDRAM before read + * mask compare pattern with mask; + * mask_pattern Mask to compare pattern + * + * Notes: + * Returns: MV_OK if success, other error code if fail. + */ +int ddr3_sdram_dm_compare(MV_DRAM_INFO *dram_info, u32 unlock_pup, + u32 *new_locked_pup, u32 *pattern, + u32 sdram_offset) +{ + u32 uj, uk, var1, var2, pup_groups; + u32 val; + u32 pup = 0; + + if (dram_info->num_of_std_pups == PUP_NUM_64BIT) + pup_groups = 2; + else + pup_groups = 1; + + ddr3_dram_sram_burst((u32)pattern, SDRAM_PBS_TX_OFFS, + LEN_PBS_PATTERN); + ddr3_dram_sram_burst(SDRAM_PBS_TX_OFFS, (u32)sdram_data, + LEN_PBS_PATTERN); + + /* Validate the correctness of the results */ + for (uj = 0; uj < LEN_PBS_PATTERN; uj++) + compare_pattern_v1(uj, &pup, pattern, pup_groups, 0); + + /* Test the DM Signals */ + *(u32 *)(SDRAM_PBS_TX_OFFS + 0x10) = 0x12345678; + *(u32 *)(SDRAM_PBS_TX_OFFS + 0x14) = 0x12345678; + + sdram_data[0] = *(u32 *)(SDRAM_PBS_TX_OFFS + 0x10); + sdram_data[1] = *(u32 *)(SDRAM_PBS_TX_OFFS + 0x14); + + for (uj = 0; uj < 2; uj++) { + if (((sdram_data[uj]) != (pattern[uj])) && + (*new_locked_pup != 0xFF)) { + for (uk = 0; uk < PUP_NUM_32BIT; uk++) { + val = CMP_BYTE_SHIFT * uk; + var1 = ((sdram_data[uj] >> val) & CMP_BYTE_MASK); + var2 = ((pattern[uj] >> val) & CMP_BYTE_MASK); + if (var1 != var2) { + *new_locked_pup |= (1 << (uk + + (PUP_NUM_32BIT * (uj % pup_groups)))); + *new_locked_pup |= pup; + } + } + } + } + + return MV_OK; +} + +/* + * Name: ddr3_sdram_pbs_compare + * Desc: Execute SRAM compare per PUP and DQ. + * Args: pup_locked bit array of locked pups + * is_tx Indicate whether Rx or Tx + * pbs_pattern_idx Index of PBS pattern + * pbs_curr_val The PBS value + * pbs_lock_val The value to set to locked PBS + * skew_array Global array to update with the compare results + * ai_unlock_pup_dq_array bit array of the locked / unlocked pups per dq. + * Notes: + * Returns: MV_OK if success, other error code if fail. + */ +int ddr3_sdram_pbs_compare(MV_DRAM_INFO *dram_info, u32 pup_locked, + int is_tx, u32 pbs_pattern_idx, + u32 pbs_curr_val, u32 pbs_lock_val, + u32 *skew_array, u8 *unlock_pup_dq_array, + u32 ecc) +{ + /* bit array failed dq per pup for current compare */ + u32 pbs_write_pup[DQ_NUM] = { 0 }; + u32 update_pup; /* pup as HW convention */ + u32 max_pup; /* maximal pup index */ + u32 pup_addr; + u32 ui, dq, pup; + int var1, var2; + u32 sdram_offset, pup_groups, tmp_pup; + u32 *pattern_ptr; + u32 val; + + /* Choose pattern */ + switch (dram_info->ddr_width) { +#if defined(MV88F672X) + case 16: + pattern_ptr = (u32 *)&pbs_pattern[pbs_pattern_idx]; + break; +#endif + case 32: + pattern_ptr = (u32 *)&pbs_pattern_32b[pbs_pattern_idx]; + break; +#if defined(MV88F78X60) + case 64: + pattern_ptr = (u32 *)&pbs_pattern_64b[pbs_pattern_idx]; + break; +#endif + default: + return MV_FAIL; + } + + max_pup = dram_info->num_of_std_pups; + + sdram_offset = SDRAM_PBS_I_OFFS + pbs_pattern_idx * SDRAM_PBS_NEXT_OFFS; + + if (dram_info->num_of_std_pups == PUP_NUM_64BIT) + pup_groups = 2; + else + pup_groups = 1; + + ddr3_reset_phy_read_fifo(); + + /* Check if need to write to sdram before read */ + if (is_tx == 1) { + ddr3_dram_sram_burst((u32)pattern_ptr, sdram_offset, + LEN_PBS_PATTERN); + } + + ddr3_dram_sram_read(sdram_offset, (u32)sdram_data, LEN_PBS_PATTERN); + + /* Compare read result to write */ + for (ui = 0; ui < LEN_PBS_PATTERN; ui++) { + if ((sdram_data[ui]) != (pattern_ptr[ui])) { + /* found error */ + /* error in low pup group */ + for (pup = 0; pup < PUP_NUM_32BIT; pup++) { + val = CMP_BYTE_SHIFT * pup; + var1 = ((sdram_data[ui] >> val) & + CMP_BYTE_MASK); + var2 = ((pattern_ptr[ui] >> val) & + CMP_BYTE_MASK); + + if (var1 != var2) { + if (dram_info->ddr_width > 16) { + tmp_pup = (pup + PUP_NUM_32BIT * + (ui % pup_groups)); + } else { + tmp_pup = (pup % PUP_NUM_16BIT); + } + + update_pup = (1 << tmp_pup); + if (ecc && (update_pup != 0x1)) + continue; + + /* + * Pup is failed - Go over all DQs and + * look for failures + */ + for (dq = 0; dq < DQ_NUM; dq++) { + val = tmp_pup * (1 - ecc) + + ecc * ECC_PUP; + if (((var1 >> dq) & 0x1) != + ((var2 >> dq) & 0x1)) { + if (pbs_locked_dq[val][dq] == 1 && + pbs_locked_value[val][dq] != pbs_curr_val) + continue; + + /* + * Activate write to + * update PBS to + * pbs_lock_val + */ + pbs_write_pup[dq] |= + update_pup; + + /* + * Update the + * unlock_pup_dq_array + */ + unlock_pup_dq_array[dq] &= + ~update_pup; + + /* + * Lock PBS value for + * failed bits in + * compare operation + */ + skew_array[tmp_pup * DQ_NUM + dq] = + pbs_curr_val; + } + } + } + } + } + } + + pup_addr = (is_tx == 1) ? PUP_PBS_TX : PUP_PBS_RX; + + /* Set last failed bits PBS to min / max pbs value */ + for (dq = 0; dq < DQ_NUM; dq++) { + for (pup = 0; pup < max_pup; pup++) { + if (pbs_write_pup[dq] & (1 << pup)) { + val = pup * (1 - ecc) + ecc * ECC_PUP; + if (pbs_locked_dq[val][dq] == 1 && + pbs_locked_value[val][dq] != pbs_curr_val) + continue; + + /* Mark the dq as locked */ + pbs_locked_dq[val][dq] = 1; + pbs_locked_value[val][dq] = pbs_curr_val; + ddr3_write_pup_reg(pup_addr + + pbs_dq_mapping[val][dq], + CS0, val, 0, pbs_lock_val); + } + } + } + + return MV_OK; +} +#endif + +/* + * Name: ddr3_sdram_direct_compare + * Desc: Execute compare per PUP without DMA (no burst mode) + * Args: unlock_pup Bit array of the unlock pups + * new_locked_pup Output bit array of the pups with failed compare + * pattern Pattern to compare + * pattern_len Length of pattern (in bytes) + * sdram_offset offset address to the SDRAM + * write write to the SDRAM before read + * mask compare pattern with mask; + * auiMaskPatter Mask to compare pattern + * + * Notes: + * Returns: MV_OK if success, other error code if fail. + */ +int ddr3_sdram_direct_compare(MV_DRAM_INFO *dram_info, u32 unlock_pup, + u32 *new_locked_pup, u32 *pattern, + u32 pattern_len, u32 sdram_offset, + int write, int mask, u32 *mask_pattern) +{ + u32 uj, uk, pup_groups; + u32 *sdram_addr; /* used to read from SDRAM */ + + sdram_addr = (u32 *)sdram_offset; + + if (dram_info->num_of_std_pups == PUP_NUM_64BIT) + pup_groups = 2; + else + pup_groups = 1; + + /* Check if need to write before read */ + if (write == 1) { + for (uk = 0; uk < pattern_len; uk++) { + *sdram_addr = pattern[uk]; + sdram_addr++; + } + } + + sdram_addr = (u32 *)sdram_offset; + + for (uk = 0; uk < pattern_len; uk++) { + sdram_data[uk] = *sdram_addr; + sdram_addr++; + } + + /* Compare read result to write */ + for (uj = 0; uj < pattern_len; uj++) { + if (dram_info->ddr_width > 16) { + compare_pattern_v1(uj, new_locked_pup, pattern, + pup_groups, 0); + } else { + compare_pattern_v2(uj, new_locked_pup, pattern); + } + } + + return MV_OK; +} + +/* + * Name: ddr3_dram_sram_burst + * Desc: Read from the SDRAM in burst of 64 bytes + * Args: src + * dst + * Notes: Using the XOR mechanism + * Returns: MV_OK if success, other error code if fail. + */ +int ddr3_dram_sram_burst(u32 src, u32 dst, u32 len) +{ + u32 chan, byte_count, cs_num, byte; + struct xor_channel_t channel; + + chan = 0; + byte_count = len * 4; + + /* Wait for previous transfer completion */ + while (mv_xor_state_get(chan) != MV_IDLE) + ; + + /* Build the channel descriptor */ + channel.desc = &dma_desc; + + /* Enable Address Override and set correct src and dst */ + if (src < SRAM_BASE) { + /* src is DRAM CS, dst is SRAM */ + cs_num = (src / (1 + SDRAM_CS_SIZE)); + reg_write(XOR_ADDR_OVRD_REG(0, 0), + ((cs_num << 1) | (1 << 0))); + channel.desc->src_addr0 = (src % (1 + SDRAM_CS_SIZE)); + channel.desc->dst_addr = dst; + } else { + /* src is SRAM, dst is DRAM CS */ + cs_num = (dst / (1 + SDRAM_CS_SIZE)); + reg_write(XOR_ADDR_OVRD_REG(0, 0), + ((cs_num << 25) | (1 << 24))); + channel.desc->src_addr0 = (src); + channel.desc->dst_addr = (dst % (1 + SDRAM_CS_SIZE)); + channel.desc->src_addr0 = src; + channel.desc->dst_addr = (dst % (1 + SDRAM_CS_SIZE)); + } + + channel.desc->src_addr1 = 0; + channel.desc->byte_cnt = byte_count; + channel.desc->next_desc_ptr = 0; + channel.desc->status = 1 << 31; + channel.desc->desc_cmd = 0x0; + channel.desc_phys_addr = (unsigned long)&dma_desc; + + ddr3_flush_l1_line((u32)&dma_desc); + + /* Issue the transfer */ + if (mv_xor_transfer(chan, MV_DMA, channel.desc_phys_addr) != MV_OK) + return MV_FAIL; + + /* Wait for completion */ + xor_waiton_eng(chan); + + if (dst > SRAM_BASE) { + for (byte = 0; byte < byte_count; byte += 0x20) + cache_inv(dst + byte); + } + + return MV_OK; +} + +/* + * Name: ddr3_flush_l1_line + * Desc: + * Args: + * Notes: + * Returns: MV_OK if success, other error code if fail. + */ +static void ddr3_flush_l1_line(u32 line) +{ + u32 reg; + +#if defined(MV88F672X) + reg = 1; +#else + reg = reg_read(REG_SAMPLE_RESET_LOW_ADDR) & + (1 << REG_SAMPLE_RESET_CPU_ARCH_OFFS); +#ifdef MV88F67XX + reg = ~reg & (1 << REG_SAMPLE_RESET_CPU_ARCH_OFFS); +#endif +#endif + + if (reg) { + /* V7 Arch mode */ + flush_l1_v7(line); + flush_l1_v7(line + CACHE_LINE_SIZE); + } else { + /* V6 Arch mode */ + flush_l1_v6(line); + flush_l1_v6(line + CACHE_LINE_SIZE); + } +} + +int ddr3_dram_sram_read(u32 src, u32 dst, u32 len) +{ + u32 ui; + u32 *dst_ptr, *src_ptr; + + dst_ptr = (u32 *)dst; + src_ptr = (u32 *)src; + + for (ui = 0; ui < len; ui++) { + *dst_ptr = *src_ptr; + dst_ptr++; + src_ptr++; + } + + return MV_OK; +} + +int ddr3_sdram_dqs_compare(MV_DRAM_INFO *dram_info, u32 unlock_pup, + u32 *new_locked_pup, u32 *pattern, + u32 pattern_len, u32 sdram_offset, int write, + int mask, u32 *mask_pattern, + int special_compare) +{ + u32 uj, pup_groups; + + if (dram_info->num_of_std_pups == PUP_NUM_64BIT) + pup_groups = 2; + else + pup_groups = 1; + + ddr3_reset_phy_read_fifo(); + + /* Check if need to write to sdram before read */ + if (write == 1) + ddr3_dram_sram_burst((u32)pattern, sdram_offset, pattern_len); + + ddr3_dram_sram_burst(sdram_offset, (u32)sdram_data, pattern_len); + + /* Compare read result to write */ + for (uj = 0; uj < pattern_len; uj++) { + if (special_compare && special_compare_pattern(uj)) + continue; + + if (dram_info->ddr_width > 16) { + compare_pattern_v1(uj, new_locked_pup, pattern, + pup_groups, 1); + } else { + compare_pattern_v2(uj, new_locked_pup, pattern); + } + } + + return MV_OK; +} + +void ddr3_reset_phy_read_fifo(void) +{ + u32 reg; + + /* reset read FIFO */ + reg = reg_read(REG_DRAM_TRAINING_ADDR); + /* Start Auto Read Leveling procedure */ + reg |= (1 << REG_DRAM_TRAINING_RL_OFFS); + + /* 0x15B0 - Training Register */ + reg_write(REG_DRAM_TRAINING_ADDR, reg); + + reg = reg_read(REG_DRAM_TRAINING_2_ADDR); + reg |= ((1 << REG_DRAM_TRAINING_2_FIFO_RST_OFFS) + + (1 << REG_DRAM_TRAINING_2_SW_OVRD_OFFS)); + + /* [0] = 1 - Enable SW override, [4] = 1 - FIFO reset */ + /* 0x15B8 - Training SW 2 Register */ + reg_write(REG_DRAM_TRAINING_2_ADDR, reg); + + do { + reg = reg_read(REG_DRAM_TRAINING_2_ADDR) & + (1 << REG_DRAM_TRAINING_2_FIFO_RST_OFFS); + } while (reg); /* Wait for '0' */ + + reg = reg_read(REG_DRAM_TRAINING_ADDR); + + /* Clear Auto Read Leveling procedure */ + reg &= ~(1 << REG_DRAM_TRAINING_RL_OFFS); + + /* 0x15B0 - Training Register */ + reg_write(REG_DRAM_TRAINING_ADDR, reg); +} diff --git a/drivers/ddr/mvebu/ddr3_spd.c b/drivers/ddr/mvebu/ddr3_spd.c new file mode 100644 index 0000000..f4f94c5 --- /dev/null +++ b/drivers/ddr/mvebu/ddr3_spd.c @@ -0,0 +1,1300 @@ +/* + * Copyright (C) Marvell International Ltd. and its affiliates + * + * SPDX-License-Identifier: GPL-2.0 + */ + +#include <common.h> +#include <i2c.h> +#include <spl.h> +#include <asm/io.h> +#include <asm/arch/cpu.h> +#include <asm/arch/soc.h> + +#include "ddr3_init.h" + +#if defined(MV88F78X60) +#include "ddr3_axp_config.h" +#elif defined(MV88F67XX) +#include "ddr3_a370_config.h" +#endif + +#if defined(MV88F672X) +#include "ddr3_a375_config.h" +#endif + +#ifdef DUNIT_SPD + +/* DIMM SPD offsets */ +#define SPD_DEV_TYPE_BYTE 2 + +#define SPD_MODULE_TYPE_BYTE 3 +#define SPD_MODULE_MASK 0xf +#define SPD_MODULE_TYPE_RDIMM 1 +#define SPD_MODULE_TYPE_UDIMM 2 + +#define SPD_DEV_DENSITY_BYTE 4 +#define SPD_DEV_DENSITY_MASK 0xf + +#define SPD_ROW_NUM_BYTE 5 +#define SPD_ROW_NUM_MIN 12 +#define SPD_ROW_NUM_OFF 3 +#define SPD_ROW_NUM_MASK (7 << SPD_ROW_NUM_OFF) + +#define SPD_COL_NUM_BYTE 5 +#define SPD_COL_NUM_MIN 9 +#define SPD_COL_NUM_OFF 0 +#define SPD_COL_NUM_MASK (7 << SPD_COL_NUM_OFF) + +#define SPD_MODULE_ORG_BYTE 7 +#define SPD_MODULE_SDRAM_DEV_WIDTH_OFF 0 +#define SPD_MODULE_SDRAM_DEV_WIDTH_MASK (7 << SPD_MODULE_SDRAM_DEV_WIDTH_OFF) +#define SPD_MODULE_BANK_NUM_MIN 1 +#define SPD_MODULE_BANK_NUM_OFF 3 +#define SPD_MODULE_BANK_NUM_MASK (7 << SPD_MODULE_BANK_NUM_OFF) + +#define SPD_BUS_WIDTH_BYTE 8 +#define SPD_BUS_WIDTH_OFF 0 +#define SPD_BUS_WIDTH_MASK (7 << SPD_BUS_WIDTH_OFF) +#define SPD_BUS_ECC_OFF 3 +#define SPD_BUS_ECC_MASK (3 << SPD_BUS_ECC_OFF) + +#define SPD_MTB_DIVIDEND_BYTE 10 +#define SPD_MTB_DIVISOR_BYTE 11 +#define SPD_TCK_BYTE 12 +#define SPD_SUP_CAS_LAT_LSB_BYTE 14 +#define SPD_SUP_CAS_LAT_MSB_BYTE 15 +#define SPD_TAA_BYTE 16 +#define SPD_TWR_BYTE 17 +#define SPD_TRCD_BYTE 18 +#define SPD_TRRD_BYTE 19 +#define SPD_TRP_BYTE 20 + +#define SPD_TRAS_MSB_BYTE 21 +#define SPD_TRAS_MSB_MASK 0xf + +#define SPD_TRC_MSB_BYTE 21 +#define SPD_TRC_MSB_MASK 0xf0 + +#define SPD_TRAS_LSB_BYTE 22 +#define SPD_TRC_LSB_BYTE 23 +#define SPD_TRFC_LSB_BYTE 24 +#define SPD_TRFC_MSB_BYTE 25 +#define SPD_TWTR_BYTE 26 +#define SPD_TRTP_BYTE 27 + +#define SPD_TFAW_MSB_BYTE 28 +#define SPD_TFAW_MSB_MASK 0xf + +#define SPD_TFAW_LSB_BYTE 29 +#define SPD_OPT_FEATURES_BYTE 30 +#define SPD_THERMAL_REFRESH_OPT_BYTE 31 + +#define SPD_ADDR_MAP_BYTE 63 +#define SPD_ADDR_MAP_MIRROR_OFFS 0 + +#define SPD_RDIMM_RC_BYTE 69 +#define SPD_RDIMM_RC_NIBBLE_MASK 0xF +#define SPD_RDIMM_RC_NUM 16 + +/* Dimm Memory Type values */ +#define SPD_MEM_TYPE_SDRAM 0x4 +#define SPD_MEM_TYPE_DDR1 0x7 +#define SPD_MEM_TYPE_DDR2 0x8 +#define SPD_MEM_TYPE_DDR3 0xB + +#define DIMM_MODULE_MANU_OFFS 64 +#define DIMM_MODULE_MANU_SIZE 8 +#define DIMM_MODULE_VEN_OFFS 73 +#define DIMM_MODULE_VEN_SIZE 25 +#define DIMM_MODULE_ID_OFFS 99 +#define DIMM_MODULE_ID_SIZE 18 + +/* enumeration for voltage levels. */ +enum dimm_volt_if { + TTL_5V_TOLERANT, + LVTTL, + HSTL_1_5V, + SSTL_3_3V, + SSTL_2_5V, + VOLTAGE_UNKNOWN, +}; + +/* enumaration for SDRAM CAS Latencies. */ +enum dimm_sdram_cas { + SD_CL_1 = 1, + SD_CL_2, + SD_CL_3, + SD_CL_4, + SD_CL_5, + SD_CL_6, + SD_CL_7, + SD_FAULT +}; + +/* enumeration for memory types */ +enum memory_type { + MEM_TYPE_SDRAM, + MEM_TYPE_DDR1, + MEM_TYPE_DDR2, + MEM_TYPE_DDR3 +}; + +/* DIMM information structure */ +typedef struct dimm_info { + /* DIMM dimensions */ + u32 num_of_module_ranks; + u32 data_width; + u32 rank_capacity; + u32 num_of_devices; + + u32 sdram_width; + u32 num_of_banks_on_each_device; + u32 sdram_capacity; + + u32 num_of_row_addr; + u32 num_of_col_addr; + + u32 addr_mirroring; + + u32 err_check_type; /* ECC , PARITY.. */ + u32 type_info; /* DDR2 only */ + + /* DIMM timing parameters */ + u32 supported_cas_latencies; + u32 refresh_interval; + u32 min_cycle_time; + u32 min_row_precharge_time; + u32 min_row_active_to_row_active; + u32 min_ras_to_cas_delay; + u32 min_write_recovery_time; /* DDR3/2 only */ + u32 min_write_to_read_cmd_delay; /* DDR3/2 only */ + u32 min_read_to_prech_cmd_delay; /* DDR3/2 only */ + u32 min_active_to_precharge; + u32 min_refresh_recovery; /* DDR3/2 only */ + u32 min_cas_lat_time; + u32 min_four_active_win_delay; + u8 dimm_rc[SPD_RDIMM_RC_NUM]; + + /* DIMM vendor ID */ + u32 vendor; +} MV_DIMM_INFO; + +static int ddr3_spd_sum_init(MV_DIMM_INFO *info, MV_DIMM_INFO *sum_info, + u32 dimm); +static u32 ddr3_get_max_val(u32 spd_val, u32 dimm_num, u32 static_val); +static u32 ddr3_get_min_val(u32 spd_val, u32 dimm_num, u32 static_val); +static int ddr3_spd_init(MV_DIMM_INFO *info, u32 dimm_addr, u32 dimm_width); +static u32 ddr3_div(u32 val, u32 divider, u32 sub); + +extern u8 spd_data[SPD_SIZE]; +extern u32 odt_config[ODT_OPT]; +extern u16 odt_static[ODT_OPT][MAX_CS]; +extern u16 odt_dynamic[ODT_OPT][MAX_CS]; + +#if !(defined(DB_88F6710) || defined(DB_88F6710_PCAC) || defined(RD_88F6710)) +/* + * Name: ddr3_get_dimm_num - Find number of dimms and their addresses + * Desc: + * Args: dimm_addr - array of dimm addresses + * Notes: + * Returns: None. + */ +static u32 ddr3_get_dimm_num(u32 *dimm_addr) +{ + u32 dimm_cur_addr; + u8 data[3]; + u32 dimm_num = 0; + int ret; + + /* Read the dimm eeprom */ + for (dimm_cur_addr = MAX_DIMM_ADDR; dimm_cur_addr > MIN_DIMM_ADDR; + dimm_cur_addr--) { + data[SPD_DEV_TYPE_BYTE] = 0; + + /* Far-End DIMM must be connected */ + if ((dimm_num == 0) && (dimm_cur_addr < FAR_END_DIMM_ADDR)) + return 0; + + ret = i2c_read(dimm_cur_addr, 0, 1, (uchar *)data, 3); + if (!ret) { + if (data[SPD_DEV_TYPE_BYTE] == SPD_MEM_TYPE_DDR3) { + dimm_addr[dimm_num] = dimm_cur_addr; + dimm_num++; + } + } + } + + return dimm_num; +} +#endif + +/* + * Name: dimmSpdInit - Get the SPD parameters. + * Desc: Read the DIMM SPD parameters into given struct parameter. + * Args: dimmNum - DIMM number. See MV_BOARD_DIMM_NUM enumerator. + * info - DIMM information structure. + * Notes: + * Returns: MV_OK if function could read DIMM parameters, 0 otherwise. + */ +int ddr3_spd_init(MV_DIMM_INFO *info, u32 dimm_addr, u32 dimm_width) +{ + u32 tmp; + u32 time_base; + int ret; + __maybe_unused u32 rc; + __maybe_unused u8 vendor_high, vendor_low; + + if (dimm_addr != 0) { + memset(spd_data, 0, SPD_SIZE * sizeof(u8)); + + ret = i2c_read(dimm_addr, 0, 1, (uchar *)spd_data, SPD_SIZE); + if (ret) + return MV_DDR3_TRAINING_ERR_TWSI_FAIL; + } + + /* Check if DDR3 */ + if (spd_data[SPD_DEV_TYPE_BYTE] != SPD_MEM_TYPE_DDR3) + return MV_DDR3_TRAINING_ERR_TWSI_BAD_TYPE; + + /* Error Check Type */ + /* No byte for error check in DDR3 SPD, use DDR2 convention */ + info->err_check_type = 0; + + /* Check if ECC */ + if ((spd_data[SPD_BUS_WIDTH_BYTE] & 0x18) >> 3) + info->err_check_type = 1; + + DEBUG_INIT_FULL_C("DRAM err_check_type ", info->err_check_type, 1); + switch (spd_data[SPD_MODULE_TYPE_BYTE]) { + case 1: + /* support RDIMM */ + info->type_info = SPD_MODULE_TYPE_RDIMM; + break; + case 2: + /* support UDIMM */ + info->type_info = SPD_MODULE_TYPE_UDIMM; + break; + case 11: /* LRDIMM current not supported */ + default: + info->type_info = (spd_data[SPD_MODULE_TYPE_BYTE]); + break; + } + + /* Size Calculations: */ + + /* Number Of Row Addresses - 12/13/14/15/16 */ + info->num_of_row_addr = + (spd_data[SPD_ROW_NUM_BYTE] & SPD_ROW_NUM_MASK) >> + SPD_ROW_NUM_OFF; + info->num_of_row_addr += SPD_ROW_NUM_MIN; + DEBUG_INIT_FULL_C("DRAM num_of_row_addr ", info->num_of_row_addr, 2); + + /* Number Of Column Addresses - 9/10/11/12 */ + info->num_of_col_addr = + (spd_data[SPD_COL_NUM_BYTE] & SPD_COL_NUM_MASK) >> + SPD_COL_NUM_OFF; + info->num_of_col_addr += SPD_COL_NUM_MIN; + DEBUG_INIT_FULL_C("DRAM num_of_col_addr ", info->num_of_col_addr, 1); + + /* Number Of Ranks = number of CS on Dimm - 1/2/3/4 Ranks */ + info->num_of_module_ranks = + (spd_data[SPD_MODULE_ORG_BYTE] & SPD_MODULE_BANK_NUM_MASK) >> + SPD_MODULE_BANK_NUM_OFF; + info->num_of_module_ranks += SPD_MODULE_BANK_NUM_MIN; + DEBUG_INIT_FULL_C("DRAM numOfModuleBanks ", info->num_of_module_ranks, + 1); + + /* Data Width - 8/16/32/64 bits */ + info->data_width = + 1 << (3 + (spd_data[SPD_BUS_WIDTH_BYTE] & SPD_BUS_WIDTH_MASK)); + DEBUG_INIT_FULL_C("DRAM data_width ", info->data_width, 1); + + /* Number Of Banks On Each Device - 8/16/32/64 banks */ + info->num_of_banks_on_each_device = + 1 << (3 + ((spd_data[SPD_DEV_DENSITY_BYTE] >> 4) & 0x7)); + DEBUG_INIT_FULL_C("DRAM num_of_banks_on_each_device ", + info->num_of_banks_on_each_device, 1); + + /* Total SDRAM capacity - 256Mb/512Mb/1Gb/2Gb/4Gb/8Gb/16Gb - MegaBits */ + info->sdram_capacity = + spd_data[SPD_DEV_DENSITY_BYTE] & SPD_DEV_DENSITY_MASK; + + /* Sdram Width - 4/8/16/32 bits */ + info->sdram_width = 1 << (2 + (spd_data[SPD_MODULE_ORG_BYTE] & + SPD_MODULE_SDRAM_DEV_WIDTH_MASK)); + DEBUG_INIT_FULL_C("DRAM sdram_width ", info->sdram_width, 1); + + /* CS (Rank) Capacity - MB */ + /* + * DDR3 device uiDensity val are: (device capacity/8) * + * (Module_width/Device_width) + */ + /* Jedec SPD DDR3 - page 7, Save spd_data in Mb - 2048=2GB */ + if (dimm_width == 32) { + info->rank_capacity = + ((1 << info->sdram_capacity) * 256 * + (info->data_width / info->sdram_width)) << 16; + /* CS size = CS size / 2 */ + } else { + info->rank_capacity = + ((1 << info->sdram_capacity) * 256 * + (info->data_width / info->sdram_width) * 0x2) << 16; + /* 0x2 => 0x100000-1Mbit / 8-bit->byte / 0x10000 */ + } + DEBUG_INIT_FULL_C("DRAM rank_capacity[31] ", info->rank_capacity, 1); + + /* Number of devices includeing Error correction */ + info->num_of_devices = + ((info->data_width / info->sdram_width) * + info->num_of_module_ranks) + info->err_check_type; + DEBUG_INIT_FULL_C("DRAM num_of_devices ", info->num_of_devices, 1); + + /* Address Mapping from Edge connector to DRAM - mirroring option */ + info->addr_mirroring = + spd_data[SPD_ADDR_MAP_BYTE] & (1 << SPD_ADDR_MAP_MIRROR_OFFS); + + /* Timings - All in ps */ + + time_base = (1000 * spd_data[SPD_MTB_DIVIDEND_BYTE]) / + spd_data[SPD_MTB_DIVISOR_BYTE]; + + /* Minimum Cycle Time At Max CasLatancy */ + info->min_cycle_time = spd_data[SPD_TCK_BYTE] * time_base; + DEBUG_INIT_FULL_C("DRAM tCKmin ", info->min_cycle_time, 1); + + /* Refresh Interval */ + /* No byte for refresh interval in DDR3 SPD, use DDR2 convention */ + /* + * JEDEC param are 0 <= Tcase <= 85: 7.8uSec, 85 <= Tcase + * <= 95: 3.9uSec + */ + info->refresh_interval = 7800000; /* Set to 7.8uSec */ + DEBUG_INIT_FULL_C("DRAM refresh_interval ", info->refresh_interval, 1); + + /* Suported Cas Latencies - DDR 3: */ + + /* + * bit7 | bit6 | bit5 | bit4 | bit3 | bit2 | bit1 | bit0 * + *******-******-******-******-******-******-******-*******-******* + CAS = 11 | 10 | 9 | 8 | 7 | 6 | 5 | 4 * + *********************************************************-******* + *******-******-******-******-******-******-******-*******-******* + * bit15 |bit14 |bit13 |bit12 |bit11 |bit10 | bit9 | bit8 * + *******-******-******-******-******-******-******-*******-******* + CAS = TBD | 18 | 17 | 16 | 15 | 14 | 13 | 12 * + */ + + /* DDR3 include 2 byte of CAS support */ + info->supported_cas_latencies = + (spd_data[SPD_SUP_CAS_LAT_MSB_BYTE] << 8) | + spd_data[SPD_SUP_CAS_LAT_LSB_BYTE]; + DEBUG_INIT_FULL_C("DRAM supported_cas_latencies ", + info->supported_cas_latencies, 1); + + /* Minimum Cycle Time At Max CasLatancy */ + info->min_cas_lat_time = (spd_data[SPD_TAA_BYTE] * time_base); + /* + * This field divided by the cycleTime will give us the CAS latency + * to config + */ + + /* + * For DDR3 and DDR2 includes Write Recovery Time field. + * Other SDRAM ignore + */ + info->min_write_recovery_time = spd_data[SPD_TWR_BYTE] * time_base; + DEBUG_INIT_FULL_C("DRAM min_write_recovery_time ", + info->min_write_recovery_time, 1); + + /* Mininmum Ras to Cas Delay */ + info->min_ras_to_cas_delay = spd_data[SPD_TRCD_BYTE] * time_base; + DEBUG_INIT_FULL_C("DRAM min_ras_to_cas_delay ", + info->min_ras_to_cas_delay, 1); + + /* Minimum Row Active to Row Active Time */ + info->min_row_active_to_row_active = + spd_data[SPD_TRRD_BYTE] * time_base; + DEBUG_INIT_FULL_C("DRAM min_row_active_to_row_active ", + info->min_row_active_to_row_active, 1); + + /* Minimum Row Precharge Delay Time */ + info->min_row_precharge_time = spd_data[SPD_TRP_BYTE] * time_base; + DEBUG_INIT_FULL_C("DRAM min_row_precharge_time ", + info->min_row_precharge_time, 1); + + /* Minimum Active to Precharge Delay Time - tRAS ps */ + info->min_active_to_precharge = + (spd_data[SPD_TRAS_MSB_BYTE] & SPD_TRAS_MSB_MASK) << 8; + info->min_active_to_precharge |= spd_data[SPD_TRAS_LSB_BYTE]; + info->min_active_to_precharge *= time_base; + DEBUG_INIT_FULL_C("DRAM min_active_to_precharge ", + info->min_active_to_precharge, 1); + + /* Minimum Refresh Recovery Delay Time - tRFC ps */ + info->min_refresh_recovery = spd_data[SPD_TRFC_MSB_BYTE] << 8; + info->min_refresh_recovery |= spd_data[SPD_TRFC_LSB_BYTE]; + info->min_refresh_recovery *= time_base; + DEBUG_INIT_FULL_C("DRAM min_refresh_recovery ", + info->min_refresh_recovery, 1); + + /* + * For DDR3 and DDR2 includes Internal Write To Read Command Delay + * field. + */ + info->min_write_to_read_cmd_delay = spd_data[SPD_TWTR_BYTE] * time_base; + DEBUG_INIT_FULL_C("DRAM min_write_to_read_cmd_delay ", + info->min_write_to_read_cmd_delay, 1); + + /* + * For DDR3 and DDR2 includes Internal Read To Precharge Command Delay + * field. + */ + info->min_read_to_prech_cmd_delay = spd_data[SPD_TRTP_BYTE] * time_base; + DEBUG_INIT_FULL_C("DRAM min_read_to_prech_cmd_delay ", + info->min_read_to_prech_cmd_delay, 1); + + /* + * For DDR3 includes Minimum Activate to Activate/Refresh Command + * field + */ + tmp = ((spd_data[SPD_TFAW_MSB_BYTE] & SPD_TFAW_MSB_MASK) << 8) | + spd_data[SPD_TFAW_LSB_BYTE]; + info->min_four_active_win_delay = tmp * time_base; + DEBUG_INIT_FULL_C("DRAM min_four_active_win_delay ", + info->min_four_active_win_delay, 1); + +#if defined(MV88F78X60) || defined(MV88F672X) + /* Registered DIMM support */ + if (info->type_info == SPD_MODULE_TYPE_RDIMM) { + for (rc = 2; rc < 6; rc += 2) { + tmp = spd_data[SPD_RDIMM_RC_BYTE + rc / 2]; + info->dimm_rc[rc] = + spd_data[SPD_RDIMM_RC_BYTE + rc / 2] & + SPD_RDIMM_RC_NIBBLE_MASK; + info->dimm_rc[rc + 1] = + (spd_data[SPD_RDIMM_RC_BYTE + rc / 2] >> 4) & + SPD_RDIMM_RC_NIBBLE_MASK; + } + + vendor_low = spd_data[66]; + vendor_high = spd_data[65]; + info->vendor = (vendor_high << 8) + vendor_low; + DEBUG_INIT_C("DDR3 Training Sequence - Registered DIMM vendor ID 0x", + info->vendor, 4); + + info->dimm_rc[0] = RDIMM_RC0; + info->dimm_rc[1] = RDIMM_RC1; + info->dimm_rc[2] = RDIMM_RC2; + info->dimm_rc[8] = RDIMM_RC8; + info->dimm_rc[9] = RDIMM_RC9; + info->dimm_rc[10] = RDIMM_RC10; + info->dimm_rc[11] = RDIMM_RC11; + } +#endif + + return MV_OK; +} + +/* + * Name: ddr3_spd_sum_init - Get the SPD parameters. + * Desc: Read the DIMM SPD parameters into given struct parameter. + * Args: dimmNum - DIMM number. See MV_BOARD_DIMM_NUM enumerator. + * info - DIMM information structure. + * Notes: + * Returns: MV_OK if function could read DIMM parameters, 0 otherwise. + */ +int ddr3_spd_sum_init(MV_DIMM_INFO *info, MV_DIMM_INFO *sum_info, u32 dimm) +{ + if (dimm == 0) { + memcpy(sum_info, info, sizeof(MV_DIMM_INFO)); + return MV_OK; + } + if (sum_info->type_info != info->type_info) { + DEBUG_INIT_S("DDR3 Dimm Compare - DIMM type does not match - FAIL\n"); + return MV_DDR3_TRAINING_ERR_DIMM_TYPE_NO_MATCH; + } + if (sum_info->err_check_type > info->err_check_type) { + sum_info->err_check_type = info->err_check_type; + DEBUG_INIT_S("DDR3 Dimm Compare - ECC does not match. ECC is disabled\n"); + } + if (sum_info->data_width != info->data_width) { + DEBUG_INIT_S("DDR3 Dimm Compare - DRAM bus width does not match - FAIL\n"); + return MV_DDR3_TRAINING_ERR_BUS_WIDTH_NOT_MATCH; + } + if (sum_info->min_cycle_time < info->min_cycle_time) + sum_info->min_cycle_time = info->min_cycle_time; + if (sum_info->refresh_interval < info->refresh_interval) + sum_info->refresh_interval = info->refresh_interval; + sum_info->supported_cas_latencies &= info->supported_cas_latencies; + if (sum_info->min_cas_lat_time < info->min_cas_lat_time) + sum_info->min_cas_lat_time = info->min_cas_lat_time; + if (sum_info->min_write_recovery_time < info->min_write_recovery_time) + sum_info->min_write_recovery_time = + info->min_write_recovery_time; + if (sum_info->min_ras_to_cas_delay < info->min_ras_to_cas_delay) + sum_info->min_ras_to_cas_delay = info->min_ras_to_cas_delay; + if (sum_info->min_row_active_to_row_active < + info->min_row_active_to_row_active) + sum_info->min_row_active_to_row_active = + info->min_row_active_to_row_active; + if (sum_info->min_row_precharge_time < info->min_row_precharge_time) + sum_info->min_row_precharge_time = info->min_row_precharge_time; + if (sum_info->min_active_to_precharge < info->min_active_to_precharge) + sum_info->min_active_to_precharge = + info->min_active_to_precharge; + if (sum_info->min_refresh_recovery < info->min_refresh_recovery) + sum_info->min_refresh_recovery = info->min_refresh_recovery; + if (sum_info->min_write_to_read_cmd_delay < + info->min_write_to_read_cmd_delay) + sum_info->min_write_to_read_cmd_delay = + info->min_write_to_read_cmd_delay; + if (sum_info->min_read_to_prech_cmd_delay < + info->min_read_to_prech_cmd_delay) + sum_info->min_read_to_prech_cmd_delay = + info->min_read_to_prech_cmd_delay; + if (sum_info->min_four_active_win_delay < + info->min_four_active_win_delay) + sum_info->min_four_active_win_delay = + info->min_four_active_win_delay; + if (sum_info->min_write_to_read_cmd_delay < + info->min_write_to_read_cmd_delay) + sum_info->min_write_to_read_cmd_delay = + info->min_write_to_read_cmd_delay; + + return MV_OK; +} + +/* + * Name: ddr3_dunit_setup + * Desc: Set the controller with the timing values. + * Args: ecc_ena - User ECC setup + * Notes: + * Returns: + */ +int ddr3_dunit_setup(u32 ecc_ena, u32 hclk_time, u32 *ddr_width) +{ + u32 reg, tmp, cwl; + u32 ddr_clk_time; + MV_DIMM_INFO dimm_info[2]; + MV_DIMM_INFO sum_info; + u32 stat_val, spd_val; + u32 cs, cl, cs_num, cs_ena; + u32 dimm_num = 0; + int status; + u32 rc; + __maybe_unused u32 dimm_cnt, cs_count, dimm; + __maybe_unused u32 dimm_addr[2] = { 0, 0 }; + +#if defined(DB_88F6710) || defined(DB_88F6710_PCAC) || defined(RD_88F6710) + /* Armada 370 - SPD is not available on DIMM */ + /* + * Set MC registers according to Static SPD values Values - + * must be set manually + */ + /* + * We only have one optional DIMM for the DB and we already got the + * SPD matching values + */ + status = ddr3_spd_init(&dimm_info[0], 0, *ddr_width); + if (MV_OK != status) + return status; + + dimm_num = 1; + /* Use JP8 to enable multiCS support for Armada 370 DB */ + if (!ddr3_check_config(EEPROM_MODULE_ADDR, CONFIG_MULTI_CS)) + dimm_info[0].num_of_module_ranks = 1; + status = ddr3_spd_sum_init(&dimm_info[0], &sum_info, 0); + if (MV_OK != status) + return status; +#else + /* Dynamic D-Unit Setup - Read SPD values */ +#ifdef DUNIT_SPD + dimm_num = ddr3_get_dimm_num(dimm_addr); + if (dimm_num == 0) { +#ifdef MIXED_DIMM_STATIC + DEBUG_INIT_S("DDR3 Training Sequence - No DIMMs detected\n"); +#else + DEBUG_INIT_S("DDR3 Training Sequence - FAILED (Wrong DIMMs Setup)\n"); + return MV_DDR3_TRAINING_ERR_BAD_DIMM_SETUP; +#endif + } else { + DEBUG_INIT_C("DDR3 Training Sequence - Number of DIMMs detected: ", + dimm_num, 1); + } + + for (dimm = 0; dimm < dimm_num; dimm++) { + status = ddr3_spd_init(&dimm_info[dimm], dimm_addr[dimm], + *ddr_width); + if (MV_OK != status) + return status; + status = ddr3_spd_sum_init(&dimm_info[dimm], &sum_info, dimm); + if (MV_OK != status) + return status; + } +#endif +#endif + + /* Set number of enabled CS */ + cs_num = 0; +#ifdef DUNIT_STATIC + cs_num = ddr3_get_cs_num_from_reg(); +#endif +#ifdef DUNIT_SPD + for (dimm = 0; dimm < dimm_num; dimm++) + cs_num += dimm_info[dimm].num_of_module_ranks; +#endif + if (cs_num > MAX_CS) { + DEBUG_INIT_C("DDR3 Training Sequence - Number of CS exceed limit - ", + MAX_CS, 1); + return MV_DDR3_TRAINING_ERR_MAX_CS_LIMIT; + } + + /* Set bitmap of enabled CS */ + cs_ena = 0; +#ifdef DUNIT_STATIC + cs_ena = ddr3_get_cs_ena_from_reg(); +#endif +#ifdef DUNIT_SPD + dimm = 0; + + if (dimm_num) { + for (cs = 0; cs < MAX_CS; cs += 2) { + if (((1 << cs) & DIMM_CS_BITMAP) && + !(cs_ena & (1 << cs))) { + if (dimm_info[dimm].num_of_module_ranks == 1) + cs_ena |= (0x1 << cs); + else if (dimm_info[dimm].num_of_module_ranks == 2) + cs_ena |= (0x3 << cs); + else if (dimm_info[dimm].num_of_module_ranks == 3) + cs_ena |= (0x7 << cs); + else if (dimm_info[dimm].num_of_module_ranks == 4) + cs_ena |= (0xF << cs); + + dimm++; + if (dimm == dimm_num) + break; + } + } + } +#endif + + if (cs_ena > 0xF) { + DEBUG_INIT_C("DDR3 Training Sequence - Number of enabled CS exceed limit - ", + MAX_CS, 1); + return MV_DDR3_TRAINING_ERR_MAX_ENA_CS_LIMIT; + } + + DEBUG_INIT_FULL_C("DDR3 - DUNIT-SET - Number of CS = ", cs_num, 1); + + /* Check Ratio - '1' - 2:1, '0' - 1:1 */ + if (reg_read(REG_DDR_IO_ADDR) & (1 << REG_DDR_IO_CLK_RATIO_OFFS)) + ddr_clk_time = hclk_time / 2; + else + ddr_clk_time = hclk_time; + +#ifdef DUNIT_STATIC + /* Get target CL value from set register */ + reg = (reg_read(REG_DDR3_MR0_ADDR) >> 2); + reg = ((((reg >> 1) & 0xE)) | (reg & 0x1)) & 0xF; + + cl = ddr3_get_max_val(ddr3_div(sum_info.min_cas_lat_time, + ddr_clk_time, 0), + dimm_num, ddr3_valid_cl_to_cl(reg)); +#else + cl = ddr3_div(sum_info.min_cas_lat_time, ddr_clk_time, 0); +#endif + if (cl < 5) + cl = 5; + + DEBUG_INIT_FULL_C("DDR3 - DUNIT-SET - Cas Latency = ", cl, 1); + + /* {0x00001400} - DDR SDRAM Configuration Register */ + reg = 0x73004000; + stat_val = ddr3_get_static_mc_value( + REG_SDRAM_CONFIG_ADDR, REG_SDRAM_CONFIG_ECC_OFFS, 0x1, 0, 0); + if (ecc_ena && ddr3_get_min_val(sum_info.err_check_type, dimm_num, + stat_val)) { + reg |= (1 << REG_SDRAM_CONFIG_ECC_OFFS); + reg |= (1 << REG_SDRAM_CONFIG_IERR_OFFS); + DEBUG_INIT_FULL_S("DDR3 - DUNIT-SET - ECC Enabled\n"); + } else { + DEBUG_INIT_FULL_S("DDR3 - DUNIT-SET - ECC Disabled\n"); + } + + if (sum_info.type_info == SPD_MODULE_TYPE_RDIMM) { +#ifdef DUNIT_STATIC + DEBUG_INIT_S("DDR3 Training Sequence - FAIL - Illegal R-DIMM setup\n"); + return MV_DDR3_TRAINING_ERR_BAD_R_DIMM_SETUP; +#endif + reg |= (1 << REG_SDRAM_CONFIG_REGDIMM_OFFS); + DEBUG_INIT_FULL_S("DDR3 - DUNIT-SET - R-DIMM\n"); + } else { + DEBUG_INIT_FULL_S("DDR3 - DUNIT-SET - U-DIMM\n"); + } + +#ifndef MV88F67XX +#ifdef DUNIT_STATIC + if (ddr3_get_min_val(sum_info.data_width, dimm_num, BUS_WIDTH) == 64) { +#else + if (*ddr_width == 64) { +#endif + reg |= (1 << REG_SDRAM_CONFIG_WIDTH_OFFS); + DEBUG_INIT_FULL_S("DDR3 - DUNIT-SET - Datawidth - 64Bits\n"); + } else { + DEBUG_INIT_FULL_S("DDR3 - DUNIT-SET - Datawidth - 32Bits\n"); + } +#else + DEBUG_INIT_FULL_S("DDR3 - DUNIT-SET - Datawidth - 16Bits\n"); +#endif + +#if defined(MV88F672X) + if (*ddr_width == 32) { + reg |= (1 << REG_SDRAM_CONFIG_WIDTH_OFFS); + DEBUG_INIT_FULL_S("DDR3 - DUNIT-SET - Datawidth - 32Bits\n"); + } else { + DEBUG_INIT_FULL_S("DDR3 - DUNIT-SET - Datawidth - 16Bits\n"); + } +#endif + stat_val = ddr3_get_static_mc_value(REG_SDRAM_CONFIG_ADDR, 0, + REG_SDRAM_CONFIG_RFRS_MASK, 0, 0); + tmp = ddr3_get_min_val(sum_info.refresh_interval / hclk_time, + dimm_num, stat_val); + +#ifdef TREFI_USER_EN + tmp = min(TREFI_USER / hclk_time, tmp); +#endif + + DEBUG_INIT_FULL_C("DDR3 - DUNIT-SET - RefreshInterval/Hclk = ", tmp, 4); + reg |= tmp; + + if (cl != 3) + reg |= (1 << 16); /* If 2:1 need to set P2DWr */ + +#if defined(MV88F672X) + reg |= (1 << 27); /* PhyRfRST = Disable */ +#endif + reg_write(REG_SDRAM_CONFIG_ADDR, reg); + + /*{0x00001404} - DDR SDRAM Configuration Register */ + reg = 0x3630B800; +#ifdef DUNIT_SPD + reg |= (DRAM_2T << REG_DUNIT_CTRL_LOW_2T_OFFS); +#endif + reg_write(REG_DUNIT_CTRL_LOW_ADDR, reg); + + /* {0x00001408} - DDR SDRAM Timing (Low) Register */ + reg = 0x0; + + /* tRAS - (0:3,20) */ + spd_val = ddr3_div(sum_info.min_active_to_precharge, + ddr_clk_time, 1); + stat_val = ddr3_get_static_mc_value(REG_SDRAM_TIMING_LOW_ADDR, + 0, 0xF, 16, 0x10); + tmp = ddr3_get_max_val(spd_val, dimm_num, stat_val); + DEBUG_INIT_FULL_C("DDR3 - DUNIT-SET - tRAS-1 = ", tmp, 1); + reg |= (tmp & 0xF); + reg |= ((tmp & 0x10) << 16); /* to bit 20 */ + + /* tRCD - (4:7) */ + spd_val = ddr3_div(sum_info.min_ras_to_cas_delay, ddr_clk_time, 1); + stat_val = ddr3_get_static_mc_value(REG_SDRAM_TIMING_LOW_ADDR, + 4, 0xF, 0, 0); + tmp = ddr3_get_max_val(spd_val, dimm_num, stat_val); + DEBUG_INIT_FULL_C("DDR3 - DUNIT-SET - tRCD-1 = ", tmp, 1); + reg |= ((tmp & 0xF) << 4); + + /* tRP - (8:11) */ + spd_val = ddr3_div(sum_info.min_row_precharge_time, ddr_clk_time, 1); + stat_val = ddr3_get_static_mc_value(REG_SDRAM_TIMING_LOW_ADDR, + 8, 0xF, 0, 0); + tmp = ddr3_get_max_val(spd_val, dimm_num, stat_val); + DEBUG_INIT_FULL_C("DDR3 - DUNIT-SET - tRP-1 = ", tmp, 1); + reg |= ((tmp & 0xF) << 8); + + /* tWR - (12:15) */ + spd_val = ddr3_div(sum_info.min_write_recovery_time, ddr_clk_time, 1); + stat_val = ddr3_get_static_mc_value(REG_SDRAM_TIMING_LOW_ADDR, + 12, 0xF, 0, 0); + tmp = ddr3_get_max_val(spd_val, dimm_num, stat_val); + DEBUG_INIT_FULL_C("DDR3 - DUNIT-SET - tWR-1 = ", tmp, 1); + reg |= ((tmp & 0xF) << 12); + + /* tWTR - (16:19) */ + spd_val = ddr3_div(sum_info.min_write_to_read_cmd_delay, ddr_clk_time, 1); + stat_val = ddr3_get_static_mc_value(REG_SDRAM_TIMING_LOW_ADDR, + 16, 0xF, 0, 0); + tmp = ddr3_get_max_val(spd_val, dimm_num, stat_val); + DEBUG_INIT_FULL_C("DDR3 - DUNIT-SET - tWTR-1 = ", tmp, 1); + reg |= ((tmp & 0xF) << 16); + + /* tRRD - (24:27) */ + spd_val = ddr3_div(sum_info.min_row_active_to_row_active, ddr_clk_time, 1); + stat_val = ddr3_get_static_mc_value(REG_SDRAM_TIMING_LOW_ADDR, + 24, 0xF, 0, 0); + tmp = ddr3_get_max_val(spd_val, dimm_num, stat_val); + DEBUG_INIT_FULL_C("DDR3 - DUNIT-SET - tRRD-1 = ", tmp, 1); + reg |= ((tmp & 0xF) << 24); + + /* tRTP - (28:31) */ + spd_val = ddr3_div(sum_info.min_read_to_prech_cmd_delay, ddr_clk_time, 1); + stat_val = ddr3_get_static_mc_value(REG_SDRAM_TIMING_LOW_ADDR, + 28, 0xF, 0, 0); + tmp = ddr3_get_max_val(spd_val, dimm_num, stat_val); + DEBUG_INIT_FULL_C("DDR3 - DUNIT-SET - tRTP-1 = ", tmp, 1); + reg |= ((tmp & 0xF) << 28); + + if (cl < 7) + reg = 0x33137663; + + reg_write(REG_SDRAM_TIMING_LOW_ADDR, reg); + + /*{0x0000140C} - DDR SDRAM Timing (High) Register */ + /* Add cycles to R2R W2W */ + reg = 0x39F8FF80; + + /* tRFC - (0:6,16:18) */ + spd_val = ddr3_div(sum_info.min_refresh_recovery, ddr_clk_time, 1); + stat_val = ddr3_get_static_mc_value(REG_SDRAM_TIMING_HIGH_ADDR, + 0, 0x7F, 9, 0x380); + tmp = ddr3_get_max_val(spd_val, dimm_num, stat_val); + DEBUG_INIT_FULL_C("DDR3 - DUNIT-SET - tRFC-1 = ", tmp, 1); + reg |= (tmp & 0x7F); + reg |= ((tmp & 0x380) << 9); /* to bit 16 */ + reg_write(REG_SDRAM_TIMING_HIGH_ADDR, reg); + + /*{0x00001410} - DDR SDRAM Address Control Register */ + reg = 0x000F0000; + + /* tFAW - (24:28) */ +#if (defined(MV88F78X60) || defined(MV88F672X)) + tmp = sum_info.min_four_active_win_delay; + spd_val = ddr3_div(tmp, ddr_clk_time, 0); + stat_val = ddr3_get_static_mc_value(REG_SDRAM_ADDRESS_CTRL_ADDR, + 24, 0x3F, 0, 0); + tmp = ddr3_get_max_val(spd_val, dimm_num, stat_val); + DEBUG_INIT_FULL_C("DDR3 - DUNIT-SET - tFAW = ", tmp, 1); + reg |= ((tmp & 0x3F) << 24); +#else + tmp = sum_info.min_four_active_win_delay - + 4 * (sum_info.min_row_active_to_row_active); + spd_val = ddr3_div(tmp, ddr_clk_time, 0); + stat_val = ddr3_get_static_mc_value(REG_SDRAM_ADDRESS_CTRL_ADDR, + 24, 0x1F, 0, 0); + tmp = ddr3_get_max_val(spd_val, dimm_num, stat_val); + DEBUG_INIT_FULL_C("DDR3 - DUNIT-SET - tFAW-4*tRRD = ", tmp, 1); + reg |= ((tmp & 0x1F) << 24); +#endif + + /* SDRAM device capacity */ +#ifdef DUNIT_STATIC + reg |= (reg_read(REG_SDRAM_ADDRESS_CTRL_ADDR) & 0xF0FFFF); +#endif + +#ifdef DUNIT_SPD + cs_count = 0; + dimm_cnt = 0; + for (cs = 0; cs < MAX_CS; cs++) { + if (cs_ena & (1 << cs) & DIMM_CS_BITMAP) { + if (dimm_info[dimm_cnt].num_of_module_ranks == cs_count) { + dimm_cnt++; + cs_count = 0; + } + cs_count++; + if (dimm_info[dimm_cnt].sdram_capacity < 0x3) { + reg |= ((dimm_info[dimm_cnt].sdram_capacity + 1) << + (REG_SDRAM_ADDRESS_SIZE_OFFS + + (REG_SDRAM_ADDRESS_CTRL_STRUCT_OFFS * cs))); + } else if (dimm_info[dimm_cnt].sdram_capacity > 0x3) { + reg |= ((dimm_info[dimm_cnt].sdram_capacity & 0x3) << + (REG_SDRAM_ADDRESS_SIZE_OFFS + + (REG_SDRAM_ADDRESS_CTRL_STRUCT_OFFS * cs))); + reg |= ((dimm_info[dimm_cnt].sdram_capacity & 0x4) << + (REG_SDRAM_ADDRESS_SIZE_HIGH_OFFS + cs)); + } + } + } + + /* SDRAM device structure */ + cs_count = 0; + dimm_cnt = 0; + for (cs = 0; cs < MAX_CS; cs++) { + if (cs_ena & (1 << cs) & DIMM_CS_BITMAP) { + if (dimm_info[dimm_cnt].num_of_module_ranks == cs_count) { + dimm_cnt++; + cs_count = 0; + } + cs_count++; + if (dimm_info[dimm_cnt].sdram_width == 16) + reg |= (1 << (REG_SDRAM_ADDRESS_CTRL_STRUCT_OFFS * cs)); + } + } +#endif + reg_write(REG_SDRAM_ADDRESS_CTRL_ADDR, reg); + + /*{0x00001418} - DDR SDRAM Operation Register */ + reg = 0xF00; + for (cs = 0; cs < MAX_CS; cs++) { + if (cs_ena & (1 << cs)) + reg &= ~(1 << (cs + REG_SDRAM_OPERATION_CS_OFFS)); + } + reg_write(REG_SDRAM_OPERATION_ADDR, reg); + + /*{0x00001420} - DDR SDRAM Extended Mode Register */ + reg = 0x00000004; + reg_write(REG_SDRAM_EXT_MODE_ADDR, reg); + + /*{0x00001424} - DDR Controller Control (High) Register */ +#if (defined(MV88F78X60) || defined(MV88F672X)) + reg = 0x0000D3FF; +#else + reg = 0x0100D1FF; +#endif + reg_write(REG_DDR_CONT_HIGH_ADDR, reg); + + /*{0x0000142C} - DDR3 Timing Register */ + reg = 0x014C2F38; +#if defined(MV88F78X60) || defined(MV88F672X) + reg = 0x1FEC2F38; +#endif + reg_write(0x142C, reg); + + /*{0x00001484} - MBus CPU Block Register */ +#ifdef MV88F67XX + if (reg_read(REG_DDR_IO_ADDR) & (1 << REG_DDR_IO_CLK_RATIO_OFFS)) + reg_write(REG_MBUS_CPU_BLOCK_ADDR, 0x0000E907); +#endif + + /* + * In case of mixed dimm and on-board devices setup paramters will + * be taken statically + */ + /*{0x00001494} - DDR SDRAM ODT Control (Low) Register */ + reg = odt_config[cs_ena]; + reg_write(REG_SDRAM_ODT_CTRL_LOW_ADDR, reg); + + /*{0x00001498} - DDR SDRAM ODT Control (High) Register */ + reg = 0x00000000; + reg_write(REG_SDRAM_ODT_CTRL_HIGH_ADDR, reg); + + /*{0x0000149C} - DDR Dunit ODT Control Register */ + reg = cs_ena; + reg_write(REG_DUNIT_ODT_CTRL_ADDR, reg); + + /*{0x000014A0} - DDR Dunit ODT Control Register */ +#if defined(MV88F672X) + reg = 0x000006A9; + reg_write(REG_DRAM_FIFO_CTRL_ADDR, reg); +#endif + + /*{0x000014C0} - DRAM address and Control Driving Strenght */ + reg_write(REG_DRAM_ADDR_CTRL_DRIVE_STRENGTH_ADDR, 0x192435e9); + + /*{0x000014C4} - DRAM Data and DQS Driving Strenght */ + reg_write(REG_DRAM_DATA_DQS_DRIVE_STRENGTH_ADDR, 0xB2C35E9); + +#if (defined(MV88F78X60) || defined(MV88F672X)) + /*{0x000014CC} - DRAM Main Pads Calibration Machine Control Register */ + reg = reg_read(REG_DRAM_MAIN_PADS_CAL_ADDR); + reg_write(REG_DRAM_MAIN_PADS_CAL_ADDR, reg | (1 << 0)); +#endif + +#if defined(MV88F672X) + /* DRAM Main Pads Calibration Machine Control Register */ + /* 0x14CC[4:3] - CalUpdateControl = IntOnly */ + reg = reg_read(REG_DRAM_MAIN_PADS_CAL_ADDR); + reg &= 0xFFFFFFE7; + reg |= (1 << 3); + reg_write(REG_DRAM_MAIN_PADS_CAL_ADDR, reg); +#endif + +#ifdef DUNIT_SPD + cs_count = 0; + dimm_cnt = 0; + for (cs = 0; cs < MAX_CS; cs++) { + if ((1 << cs) & DIMM_CS_BITMAP) { + if ((1 << cs) & cs_ena) { + if (dimm_info[dimm_cnt].num_of_module_ranks == + cs_count) { + dimm_cnt++; + cs_count = 0; + } + cs_count++; + reg_write(REG_CS_SIZE_SCRATCH_ADDR + (cs * 0x8), + dimm_info[dimm_cnt].rank_capacity - 1); + } else { + reg_write(REG_CS_SIZE_SCRATCH_ADDR + (cs * 0x8), 0); + } + } + } +#endif + + /*{0x00020184} - Close FastPath - 2G */ + reg_write(REG_FASTPATH_WIN_0_CTRL_ADDR, 0); + + /*{0x00001538} - Read Data Sample Delays Register */ + reg = 0; + for (cs = 0; cs < MAX_CS; cs++) { + if (cs_ena & (1 << cs)) + reg |= (cl << (REG_READ_DATA_SAMPLE_DELAYS_OFFS * cs)); + } + + reg_write(REG_READ_DATA_SAMPLE_DELAYS_ADDR, reg); + DEBUG_INIT_FULL_C("DDR3 - SPD-SET - Read Data Sample Delays = ", reg, + 1); + + /*{0x0000153C} - Read Data Ready Delay Register */ + reg = 0; + for (cs = 0; cs < MAX_CS; cs++) { + if (cs_ena & (1 << cs)) { + reg |= ((cl + 2) << + (REG_READ_DATA_READY_DELAYS_OFFS * cs)); + } + } + reg_write(REG_READ_DATA_READY_DELAYS_ADDR, reg); + DEBUG_INIT_FULL_C("DDR3 - SPD-SET - Read Data Ready Delays = ", reg, 1); + + /* Set MR registers */ + /* MR0 */ + reg = 0x00000600; + tmp = ddr3_cl_to_valid_cl(cl); + reg |= ((tmp & 0x1) << 2); + reg |= ((tmp & 0xE) << 3); /* to bit 4 */ + for (cs = 0; cs < MAX_CS; cs++) { + if (cs_ena & (1 << cs)) { + reg_write(REG_DDR3_MR0_CS_ADDR + + (cs << MR_CS_ADDR_OFFS), reg); + } + } + + /* MR1 */ + reg = 0x00000044 & REG_DDR3_MR1_ODT_MASK; + if (cs_num > 1) + reg = 0x00000046 & REG_DDR3_MR1_ODT_MASK; + + for (cs = 0; cs < MAX_CS; cs++) { + if (cs_ena & (1 << cs)) { + reg |= odt_static[cs_ena][cs]; + reg_write(REG_DDR3_MR1_CS_ADDR + + (cs << MR_CS_ADDR_OFFS), reg); + } + } + + /* MR2 */ + if (reg_read(REG_DDR_IO_ADDR) & (1 << REG_DDR_IO_CLK_RATIO_OFFS)) + tmp = hclk_time / 2; + else + tmp = hclk_time; + + if (tmp >= 2500) + cwl = 5; /* CWL = 5 */ + else if (tmp >= 1875 && tmp < 2500) + cwl = 6; /* CWL = 6 */ + else if (tmp >= 1500 && tmp < 1875) + cwl = 7; /* CWL = 7 */ + else if (tmp >= 1250 && tmp < 1500) + cwl = 8; /* CWL = 8 */ + else if (tmp >= 1070 && tmp < 1250) + cwl = 9; /* CWL = 9 */ + else if (tmp >= 935 && tmp < 1070) + cwl = 10; /* CWL = 10 */ + else if (tmp >= 833 && tmp < 935) + cwl = 11; /* CWL = 11 */ + else if (tmp >= 750 && tmp < 833) + cwl = 12; /* CWL = 12 */ + else { + cwl = 12; /* CWL = 12 */ + printf("Unsupported hclk %d MHz\n", tmp); + } + + reg = ((cwl - 5) << REG_DDR3_MR2_CWL_OFFS); + + for (cs = 0; cs < MAX_CS; cs++) { + if (cs_ena & (1 << cs)) { + reg &= REG_DDR3_MR2_ODT_MASK; + reg |= odt_dynamic[cs_ena][cs]; + reg_write(REG_DDR3_MR2_CS_ADDR + + (cs << MR_CS_ADDR_OFFS), reg); + } + } + + /* MR3 */ + reg = 0x00000000; + for (cs = 0; cs < MAX_CS; cs++) { + if (cs_ena & (1 << cs)) { + reg_write(REG_DDR3_MR3_CS_ADDR + + (cs << MR_CS_ADDR_OFFS), reg); + } + } + + /* {0x00001428} - DDR ODT Timing (Low) Register */ + reg = 0; + reg |= (((cl - cwl + 1) & 0xF) << 4); + reg |= (((cl - cwl + 6) & 0xF) << 8); + reg |= ((((cl - cwl + 6) >> 4) & 0x1) << 21); + reg |= (((cl - 1) & 0xF) << 12); + reg |= (((cl + 6) & 0x1F) << 16); + reg_write(REG_ODT_TIME_LOW_ADDR, reg); + + /* {0x0000147C} - DDR ODT Timing (High) Register */ + reg = 0x00000071; + reg |= ((cwl - 1) << 8); + reg |= ((cwl + 5) << 12); + reg_write(REG_ODT_TIME_HIGH_ADDR, reg); + +#ifdef DUNIT_SPD + /*{0x000015E0} - DDR3 Rank Control Register */ + reg = cs_ena; + cs_count = 0; + dimm_cnt = 0; + for (cs = 0; cs < MAX_CS; cs++) { + if (cs_ena & (1 << cs) & DIMM_CS_BITMAP) { + if (dimm_info[dimm_cnt].num_of_module_ranks == cs_count) { + dimm_cnt++; + cs_count = 0; + } + cs_count++; + + if (dimm_info[dimm_cnt].addr_mirroring && + (cs == 1 || cs == 3) && + (sum_info.type_info != SPD_MODULE_TYPE_RDIMM)) { + reg |= (1 << (REG_DDR3_RANK_CTRL_MIRROR_OFFS + cs)); + DEBUG_INIT_FULL_C("DDR3 - SPD-SET - Setting Address Mirroring for CS = ", + cs, 1); + } + } + } + reg_write(REG_DDR3_RANK_CTRL_ADDR, reg); +#endif + + /*{0xD00015E4} - ZQDS Configuration Register */ + reg = 0x00203c18; + reg_write(REG_ZQC_CONF_ADDR, reg); + + /* {0x00015EC} - DDR PHY */ +#if defined(MV88F78X60) + reg = 0xF800AAA5; + if (mv_ctrl_rev_get() == MV_78XX0_B0_REV) + reg = 0xF800A225; +#else + reg = 0xDE000025; +#if defined(MV88F672X) + reg = 0xF800A225; +#endif +#endif + reg_write(REG_DRAM_PHY_CONFIG_ADDR, reg); + +#if (defined(MV88F78X60) || defined(MV88F672X)) + /* Registered DIMM support - supported only in AXP A0 devices */ + /* Currently supported for SPD detection only */ + /* + * Flow is according to the Registered DIMM chapter in the + * Functional Spec + */ + if (sum_info.type_info == SPD_MODULE_TYPE_RDIMM) { + DEBUG_INIT_S("DDR3 Training Sequence - Registered DIMM detected\n"); + + /* Set commands parity completion */ + reg = reg_read(REG_REGISTERED_DRAM_CTRL_ADDR); + reg &= ~REG_REGISTERED_DRAM_CTRL_PARITY_MASK; + reg |= 0x8; + reg_write(REG_REGISTERED_DRAM_CTRL_ADDR, reg); + + /* De-assert M_RESETn and assert M_CKE */ + reg_write(REG_SDRAM_INIT_CTRL_ADDR, + 1 << REG_SDRAM_INIT_CKE_ASSERT_OFFS); + do { + reg = (reg_read(REG_SDRAM_INIT_CTRL_ADDR)) & + (1 << REG_SDRAM_INIT_CKE_ASSERT_OFFS); + } while (reg); + + for (rc = 0; rc < SPD_RDIMM_RC_NUM; rc++) { + if (rc != 6 && rc != 7) { + /* Set CWA Command */ + reg = (REG_SDRAM_OPERATION_CMD_CWA & + ~(0xF << REG_SDRAM_OPERATION_CS_OFFS)); + reg |= ((dimm_info[0].dimm_rc[rc] & + REG_SDRAM_OPERATION_CWA_DATA_MASK) << + REG_SDRAM_OPERATION_CWA_DATA_OFFS); + reg |= rc << REG_SDRAM_OPERATION_CWA_RC_OFFS; + /* Configure - Set Delay - tSTAB/tMRD */ + if (rc == 2 || rc == 10) + reg |= (0x1 << REG_SDRAM_OPERATION_CWA_DELAY_SEL_OFFS); + /* 0x1418 - SDRAM Operation Register */ + reg_write(REG_SDRAM_OPERATION_ADDR, reg); + + /* + * Poll the "cmd" field in the SDRAM OP + * register for 0x0 + */ + do { + reg = reg_read(REG_SDRAM_OPERATION_ADDR) & + (REG_SDRAM_OPERATION_CMD_MASK); + } while (reg); + } + } + } +#endif + + return MV_OK; +} + +/* + * Name: ddr3_div - this function divides integers + * Desc: + * Args: val - the value + * divider - the divider + * sub - substruction value + * Notes: + * Returns: required value + */ +u32 ddr3_div(u32 val, u32 divider, u32 sub) +{ + return val / divider + (val % divider > 0 ? 1 : 0) - sub; +} + +/* + * Name: ddr3_get_max_val + * Desc: + * Args: + * Notes: + * Returns: + */ +u32 ddr3_get_max_val(u32 spd_val, u32 dimm_num, u32 static_val) +{ +#ifdef DUNIT_STATIC + if (dimm_num > 0) { + if (spd_val >= static_val) + return spd_val; + else + return static_val; + } else { + return static_val; + } +#else + return spd_val; +#endif +} + +/* + * Name: ddr3_get_min_val + * Desc: + * Args: + * Notes: + * Returns: + */ +u32 ddr3_get_min_val(u32 spd_val, u32 dimm_num, u32 static_val) +{ +#ifdef DUNIT_STATIC + if (dimm_num > 0) { + if (spd_val <= static_val) + return spd_val; + else + return static_val; + } else + return static_val; +#else + return spd_val; +#endif +} +#endif diff --git a/drivers/ddr/mvebu/ddr3_write_leveling.c b/drivers/ddr/mvebu/ddr3_write_leveling.c new file mode 100644 index 0000000..df3a3df --- /dev/null +++ b/drivers/ddr/mvebu/ddr3_write_leveling.c @@ -0,0 +1,1366 @@ +/* + * Copyright (C) Marvell International Ltd. and its affiliates + * + * SPDX-License-Identifier: GPL-2.0 + */ + +#include <common.h> +#include <i2c.h> +#include <spl.h> +#include <asm/io.h> +#include <asm/arch/cpu.h> +#include <asm/arch/soc.h> + +#include "ddr3_hw_training.h" + +/* + * Debug + */ +#define DEBUG_WL_C(s, d, l) \ + DEBUG_WL_S(s); DEBUG_WL_D(d, l); DEBUG_WL_S("\n") +#define DEBUG_WL_FULL_C(s, d, l) \ + DEBUG_WL_FULL_S(s); DEBUG_WL_FULL_D(d, l); DEBUG_WL_FULL_S("\n") + +#ifdef MV_DEBUG_WL +#define DEBUG_RL_S(s) \ + debug_cond(ddr3_get_log_level() >= MV_LOG_LEVEL_2, "%s", s) +#define DEBUG_RL_D(d, l) \ + debug_cond(ddr3_get_log_level() >= MV_LOG_LEVEL_2, "%x", d) +#else +#define DEBUG_WL_S(s) +#define DEBUG_WL_D(d, l) +#endif + +#ifdef MV_DEBUG_WL_FULL +#define DEBUG_WL_FULL_S(s) puts(s) +#define DEBUG_WL_FULL_D(d, l) printf("%x", d) +#else +#define DEBUG_WL_FULL_S(s) +#define DEBUG_WL_FULL_D(d, l) +#endif + +#define WL_SUP_EXPECTED_DATA 0x21 +#define WL_SUP_READ_DRAM_ENTRY 0x8 + +static int ddr3_write_leveling_single_cs(u32 cs, u32 freq, int ratio_2to1, + u32 *result, + MV_DRAM_INFO *dram_info); +static void ddr3_write_ctrl_pup_reg(int bc_acc, u32 pup, u32 reg_addr, + u32 data); + +extern u16 odt_static[ODT_OPT][MAX_CS]; +extern u16 odt_dynamic[ODT_OPT][MAX_CS]; +extern u32 wl_sup_pattern[LEN_WL_SUP_PATTERN]; + +/* + * Name: ddr3_write_leveling_hw + * Desc: Execute Write leveling phase by HW + * Args: freq - current sequence frequency + * dram_info - main struct + * Notes: + * Returns: MV_OK if success, MV_FAIL if fail. + */ +int ddr3_write_leveling_hw(u32 freq, MV_DRAM_INFO *dram_info) +{ + u32 reg, phase, delay, cs, pup; +#ifdef MV88F67XX + int dpde_flag = 0; +#endif + /* Debug message - Start Read leveling procedure */ + DEBUG_WL_S("DDR3 - Write Leveling - Starting HW WL procedure\n"); + +#ifdef MV88F67XX + /* Dynamic pad issue (BTS669) during WL */ + reg = reg_read(REG_DUNIT_CTRL_LOW_ADDR); + if (reg & (1 << REG_DUNIT_CTRL_LOW_DPDE_OFFS)) { + dpde_flag = 1; + reg_write(REG_DUNIT_CTRL_LOW_ADDR, + reg & ~(1 << REG_DUNIT_CTRL_LOW_DPDE_OFFS)); + } +#endif + + reg = 1 << REG_DRAM_TRAINING_WL_OFFS; + /* Config the retest number */ + reg |= (COUNT_HW_WL << REG_DRAM_TRAINING_RETEST_OFFS); + reg |= (dram_info->cs_ena << (REG_DRAM_TRAINING_CS_OFFS)); + reg_write(REG_DRAM_TRAINING_ADDR, reg); /* 0x15B0 - Training Register */ + + reg = reg_read(REG_DRAM_TRAINING_SHADOW_ADDR) | + (1 << REG_DRAM_TRAINING_AUTO_OFFS); + reg_write(REG_DRAM_TRAINING_SHADOW_ADDR, reg); + + /* Wait */ + do { + reg = reg_read(REG_DRAM_TRAINING_SHADOW_ADDR) & + (1 << REG_DRAM_TRAINING_AUTO_OFFS); + } while (reg); /* Wait for '0' */ + + reg = reg_read(REG_DRAM_TRAINING_ADDR); + /* Check if Successful */ + if (reg & (1 << REG_DRAM_TRAINING_ERROR_OFFS)) { + /* + * Read results to arrays - Results are required for WL + * High freq Supplement and DQS Centralization + */ + for (cs = 0; cs < MAX_CS; cs++) { + if (dram_info->cs_ena & (1 << cs)) { + for (pup = 0; + pup < dram_info->num_of_total_pups; + pup++) { + if (pup == dram_info->num_of_std_pups + && dram_info->ecc_ena) + pup = ECC_PUP; + reg = + ddr3_read_pup_reg(PUP_WL_MODE, cs, + pup); + phase = + (reg >> REG_PHY_PHASE_OFFS) & + PUP_PHASE_MASK; + delay = reg & PUP_DELAY_MASK; + dram_info->wl_val[cs][pup][P] = phase; + dram_info->wl_val[cs][pup][D] = delay; + dram_info->wl_val[cs][pup][S] = + WL_HI_FREQ_STATE - 1; + reg = + ddr3_read_pup_reg(PUP_WL_MODE + 0x1, + cs, pup); + dram_info->wl_val[cs][pup][DQS] = + (reg & 0x3F); + } + +#ifdef MV_DEBUG_WL + /* Debug message - Print res for cs[i]: cs,PUP,Phase,Delay */ + DEBUG_WL_S("DDR3 - Write Leveling - Write Leveling Cs - "); + DEBUG_WL_D((u32) cs, 1); + DEBUG_WL_S(" Results:\n"); + for (pup = 0; + pup < dram_info->num_of_total_pups; + pup++) { + if (pup == dram_info->num_of_std_pups + && dram_info->ecc_ena) + pup = ECC_PUP; + DEBUG_WL_S("DDR3 - Write Leveling - PUP: "); + DEBUG_WL_D((u32) pup, 1); + DEBUG_WL_S(", Phase: "); + DEBUG_WL_D((u32) + dram_info->wl_val[cs][pup] + [P], 1); + DEBUG_WL_S(", Delay: "); + DEBUG_WL_D((u32) + dram_info->wl_val[cs][pup] + [D], 2); + DEBUG_WL_S("\n"); + } +#endif + } + } + + /* Dynamic pad issue (BTS669) during WL */ +#ifdef MV88F67XX + if (dpde_flag) { + reg = reg_read(REG_DUNIT_CTRL_LOW_ADDR) | + (1 << REG_DUNIT_CTRL_LOW_DPDE_OFFS); + reg_write(REG_DUNIT_CTRL_LOW_ADDR, reg); + } +#endif + + DEBUG_WL_S("DDR3 - Write Leveling - HW WL Ended Successfully\n"); + + return MV_OK; + } else { + DEBUG_WL_S("DDR3 - Write Leveling - HW WL Error\n"); + return MV_FAIL; + } +} + +/* + * Name: ddr3_wl_supplement + * Desc: Write Leveling Supplement + * Args: dram_info - main struct + * Notes: + * Returns: MV_OK if success, MV_FAIL if fail. + */ +int ddr3_wl_supplement(MV_DRAM_INFO *dram_info) +{ + u32 cs, cnt, pup_num, sum, phase, delay, max_pup_num, pup, sdram_offset; + u32 tmp_count, ecc, reg; + u32 ddr_width, tmp_pup, idx; + u32 sdram_pup_val, uj; + u32 one_clk_err = 0, align_err = 0, no_err = 0, err = 0, err_n = 0; + u32 sdram_data[LEN_WL_SUP_PATTERN] __aligned(32) = { 0 }; + + ddr_width = dram_info->ddr_width; + no_err = 0; + + DEBUG_WL_S("DDR3 - Write Leveling Hi-Freq Supplement - Starting\n"); + + switch (ddr_width) { + /* Data error from pos-adge to pos-adge */ + case 16: + one_clk_err = 4; + align_err = 4; + break; + case 32: + one_clk_err = 8; + align_err = 8; + break; + case 64: + one_clk_err = 0x10; + align_err = 0x10; + break; + default: + DEBUG_WL_S("Error - bus width!!!\n"); + return MV_FAIL; + } + + /* Enable SW override */ + reg = reg_read(REG_DRAM_TRAINING_2_ADDR) | + (1 << REG_DRAM_TRAINING_2_SW_OVRD_OFFS); + + /* [0] = 1 - Enable SW override */ + /* 0x15B8 - Training SW 2 Register */ + reg_write(REG_DRAM_TRAINING_2_ADDR, reg); + DEBUG_WL_S("DDR3 - Write Leveling Hi-Freq Supplement - SW Override Enabled\n"); + reg = (1 << REG_DRAM_TRAINING_AUTO_OFFS); + reg_write(REG_DRAM_TRAINING_ADDR, reg); /* 0x15B0 - Training Register */ + tmp_count = 0; + for (cs = 0; cs < MAX_CS; cs++) { + if (dram_info->cs_ena & (1 << cs)) { + sum = 0; + /* + * 2 iterations loop: 1)actual WL results 2) fix WL + * if needed + */ + for (cnt = 0; cnt < COUNT_WL_HI_FREQ; cnt++) { + DEBUG_WL_C("COUNT = ", cnt, 1); + for (ecc = 0; ecc < (dram_info->ecc_ena + 1); + ecc++) { + if (ecc) { + DEBUG_WL_S("ECC PUP:\n"); + } else { + DEBUG_WL_S("DATA PUP:\n"); + } + + max_pup_num = + dram_info->num_of_std_pups * (1 - + ecc) + + ecc; + /* ECC Support - Switch ECC Mux on ecc=1 */ + reg = + (reg_read(REG_DRAM_TRAINING_2_ADDR) + & ~(1 << + REG_DRAM_TRAINING_2_ECC_MUX_OFFS)); + reg |= + (dram_info->ecc_ena * + ecc << + REG_DRAM_TRAINING_2_ECC_MUX_OFFS); + reg_write(REG_DRAM_TRAINING_2_ADDR, + reg); + ddr3_reset_phy_read_fifo(); + + /* Write to memory */ + sdram_offset = + tmp_count * (SDRAM_CS_SIZE + 1) + + 0x200; + if (MV_OK != ddr3_dram_sram_burst((u32) + wl_sup_pattern, + sdram_offset, + LEN_WL_SUP_PATTERN)) + return MV_FAIL; + + /* Read from memory */ + if (MV_OK != + ddr3_dram_sram_burst(sdram_offset, + (u32) + sdram_data, + LEN_WL_SUP_PATTERN)) + return MV_FAIL; + + /* Print the buffer */ + for (uj = 0; uj < LEN_WL_SUP_PATTERN; + uj++) { + if ((uj % 4 == 0) && (uj != 0)) { + DEBUG_WL_S("\n"); + } + DEBUG_WL_D(sdram_data[uj], + 8); + DEBUG_WL_S(" "); + } + + /* Check pup which DQS/DATA is error */ + for (pup = 0; pup < max_pup_num; pup++) { + /* ECC support - bit 8 */ + pup_num = (ecc) ? ECC_PUP : pup; + if (pup < 4) { /* lower 32 bit */ + tmp_pup = pup; + idx = + WL_SUP_READ_DRAM_ENTRY; + } else { /* higher 32 bit */ + tmp_pup = pup - 4; + idx = + WL_SUP_READ_DRAM_ENTRY + + 1; + } + DEBUG_WL_S("\nCS: "); + DEBUG_WL_D((u32) cs, 1); + DEBUG_WL_S(" PUP: "); + DEBUG_WL_D((u32) pup_num, 1); + DEBUG_WL_S("\n"); + sdram_pup_val = + ((sdram_data[idx] >> + ((tmp_pup) * 8)) & 0xFF); + DEBUG_WL_C("Actual Data = ", + sdram_pup_val, 2); + DEBUG_WL_C("Expected Data = ", + (WL_SUP_EXPECTED_DATA + + pup), 2); + /* + * ALINGHMENT: calculate + * expected data vs actual data + */ + err = + (WL_SUP_EXPECTED_DATA + + pup) - sdram_pup_val; + /* + * CLOCK LONG: calculate + * expected data vs actual data + */ + err_n = + sdram_pup_val - + (WL_SUP_EXPECTED_DATA + + pup); + DEBUG_WL_C("err = ", err, 2); + DEBUG_WL_C("err_n = ", err_n, + 2); + if (err == no_err) { + /* PUP is correct - increment State */ + dram_info->wl_val[cs] + [pup_num] + [S] = 1; + } else if (err_n == one_clk_err) { + /* clock is longer than DQS */ + phase = + ((dram_info->wl_val + [cs] + [pup_num][P] + + WL_HI_FREQ_SHIFT) + % MAX_PHASE_2TO1); + dram_info->wl_val[cs] + [pup_num] + [P] = phase; + delay = + dram_info->wl_val + [cs][pup_num] + [D]; + DEBUG_WL_S("#### Clock is longer than DQS more than one clk cycle ####\n"); + ddr3_write_pup_reg + (PUP_WL_MODE, cs, + pup * (1 - ecc) + + ECC_PUP * ecc, + phase, delay); + } else if (err == align_err) { + /* clock is align to DQS */ + phase = + dram_info->wl_val + [cs][pup_num] + [P]; + delay = + dram_info->wl_val + [cs][pup_num] + [D]; + DEBUG_WL_S("#### Alignment PUPS problem ####\n"); + if ((phase == 0) + || ((phase == 1) + && (delay <= + 0x10))) { + DEBUG_WL_S("#### Warning - Possible Layout Violation (DQS is longer than CLK)####\n"); + } + + phase = 0x0; + delay = 0x0; + dram_info->wl_val[cs] + [pup_num] + [P] = phase; + dram_info->wl_val[cs] + [pup_num] + [D] = delay; + ddr3_write_pup_reg + (PUP_WL_MODE, cs, + pup * (1 - ecc) + + ECC_PUP * ecc, + phase, delay); + } + /* Stop condition for ECC phase */ + pup = (ecc) ? max_pup_num : pup; + } + + /* ECC Support - Disable ECC MUX */ + reg = + (reg_read(REG_DRAM_TRAINING_2_ADDR) + & ~(1 << + REG_DRAM_TRAINING_2_ECC_MUX_OFFS)); + reg_write(REG_DRAM_TRAINING_2_ADDR, + reg); + } + } + + for (pup = 0; pup < dram_info->num_of_std_pups; pup++) + sum += dram_info->wl_val[cs][pup][S]; + + if (dram_info->ecc_ena) + sum += dram_info->wl_val[cs][ECC_PUP][S]; + + /* Checks if any pup is not locked after the change */ + if (sum < (WL_HI_FREQ_STATE * (dram_info->num_of_total_pups))) { + DEBUG_WL_C("DDR3 - Write Leveling Hi-Freq Supplement - didn't work for Cs - ", + (u32) cs, 1); + return MV_FAIL; + } + tmp_count++; + } + } + + dram_info->wl_max_phase = 0; + dram_info->wl_min_phase = 10; + + /* + * Read results to arrays - Results are required for DQS Centralization + */ + for (cs = 0; cs < MAX_CS; cs++) { + if (dram_info->cs_ena & (1 << cs)) { + for (pup = 0; pup < dram_info->num_of_total_pups; pup++) { + if (pup == dram_info->num_of_std_pups + && dram_info->ecc_ena) + pup = ECC_PUP; + reg = ddr3_read_pup_reg(PUP_WL_MODE, cs, pup); + phase = + (reg >> REG_PHY_PHASE_OFFS) & + PUP_PHASE_MASK; + if (phase > dram_info->wl_max_phase) + dram_info->wl_max_phase = phase; + if (phase < dram_info->wl_min_phase) + dram_info->wl_min_phase = phase; + } + } + } + + /* Disable SW override - Must be in a different stage */ + /* [0]=0 - Enable SW override */ + reg = reg_read(REG_DRAM_TRAINING_2_ADDR); + reg &= ~(1 << REG_DRAM_TRAINING_2_SW_OVRD_OFFS); + /* 0x15B8 - Training SW 2 Register */ + reg_write(REG_DRAM_TRAINING_2_ADDR, reg); + + reg = reg_read(REG_DRAM_TRAINING_1_ADDR) | + (1 << REG_DRAM_TRAINING_1_TRNBPOINT_OFFS); + reg_write(REG_DRAM_TRAINING_1_ADDR, reg); + + DEBUG_WL_S("DDR3 - Write Leveling Hi-Freq Supplement - Ended Successfully\n"); + + return MV_OK; +} + +/* + * Name: ddr3_write_leveling_hw_reg_dimm + * Desc: Execute Write leveling phase by HW + * Args: freq - current sequence frequency + * dram_info - main struct + * Notes: + * Returns: MV_OK if success, MV_FAIL if fail. + */ +int ddr3_write_leveling_hw_reg_dimm(u32 freq, MV_DRAM_INFO *dram_info) +{ + u32 reg, phase, delay, cs, pup, pup_num; + __maybe_unused int dpde_flag = 0; + + /* Debug message - Start Read leveling procedure */ + DEBUG_WL_S("DDR3 - Write Leveling - Starting HW WL procedure\n"); + + if (dram_info->num_cs > 2) { + DEBUG_WL_S("DDR3 - Write Leveling - HW WL Ended Successfully\n"); + return MV_NO_CHANGE; + } + + /* If target freq = 400 move clock start point */ + /* Write to control PUP to Control Deskew Regs */ + if (freq <= DDR_400) { + for (pup = 0; pup <= dram_info->num_of_total_pups; pup++) { + /* PUP_DELAY_MASK 0x1F */ + /* reg = 0x0C10001F + (uj << 16); */ + ddr3_write_ctrl_pup_reg(1, pup, CNTRL_PUP_DESKEW + pup, + 0x1F); + } + } + +#ifdef MV88F67XX + /* Dynamic pad issue (BTS669) during WL */ + reg = reg_read(REG_DUNIT_CTRL_LOW_ADDR); + if (reg & (1 << REG_DUNIT_CTRL_LOW_DPDE_OFFS)) { + dpde_flag = 1; + reg_write(REG_DUNIT_CTRL_LOW_ADDR, + reg & ~(1 << REG_DUNIT_CTRL_LOW_DPDE_OFFS)); + } +#endif + + reg = (1 << REG_DRAM_TRAINING_WL_OFFS); + /* Config the retest number */ + reg |= (COUNT_HW_WL << REG_DRAM_TRAINING_RETEST_OFFS); + reg |= (dram_info->cs_ena << (REG_DRAM_TRAINING_CS_OFFS)); + reg_write(REG_DRAM_TRAINING_ADDR, reg); /* 0x15B0 - Training Register */ + + reg = reg_read(REG_DRAM_TRAINING_SHADOW_ADDR) | + (1 << REG_DRAM_TRAINING_AUTO_OFFS); + reg_write(REG_DRAM_TRAINING_SHADOW_ADDR, reg); + + /* Wait */ + do { + reg = reg_read(REG_DRAM_TRAINING_SHADOW_ADDR) & + (1 << REG_DRAM_TRAINING_AUTO_OFFS); + } while (reg); /* Wait for '0' */ + + reg = reg_read(REG_DRAM_TRAINING_ADDR); + /* Check if Successful */ + if (reg & (1 << REG_DRAM_TRAINING_ERROR_OFFS)) { + /* + * Read results to arrays - Results are required for WL High + * freq Supplement and DQS Centralization + */ + for (cs = 0; cs < MAX_CS; cs++) { + if (dram_info->cs_ena & (1 << cs)) { + for (pup = 0; + pup < dram_info->num_of_total_pups; + pup++) { + if (pup == dram_info->num_of_std_pups + && dram_info->ecc_ena) + pup = ECC_BIT; + reg = + ddr3_read_pup_reg(PUP_WL_MODE, cs, + pup); + phase = + (reg >> REG_PHY_PHASE_OFFS) & + PUP_PHASE_MASK; + delay = reg & PUP_DELAY_MASK; + dram_info->wl_val[cs][pup][P] = phase; + dram_info->wl_val[cs][pup][D] = delay; + if ((phase == 1) && (delay >= 0x1D)) { + /* + * Need to do it here for + * uncorrect WL values + */ + ddr3_write_pup_reg(PUP_WL_MODE, + cs, pup, 0, + 0); + dram_info->wl_val[cs][pup][P] = + 0; + dram_info->wl_val[cs][pup][D] = + 0; + } + dram_info->wl_val[cs][pup][S] = + WL_HI_FREQ_STATE - 1; + reg = + ddr3_read_pup_reg(PUP_WL_MODE + 0x1, + cs, pup); + dram_info->wl_val[cs][pup][DQS] = + (reg & 0x3F); + } +#ifdef MV_DEBUG_WL + /* + * Debug message - Print res for cs[i]: + * cs,PUP,Phase,Delay + */ + DEBUG_WL_S("DDR3 - Write Leveling - Write Leveling Cs - "); + DEBUG_WL_D((u32) cs, 1); + DEBUG_WL_S(" Results:\n"); + for (pup = 0; + pup < dram_info->num_of_total_pups; + pup++) { + DEBUG_WL_S + ("DDR3 - Write Leveling - PUP: "); + DEBUG_WL_D((u32) pup, 1); + DEBUG_WL_S(", Phase: "); + DEBUG_WL_D((u32) + dram_info->wl_val[cs][pup] + [P], 1); + DEBUG_WL_S(", Delay: "); + DEBUG_WL_D((u32) + dram_info->wl_val[cs][pup] + [D], 2); + DEBUG_WL_S("\n"); + } +#endif + } + } + +#ifdef MV88F67XX + /* Dynamic pad issue (BTS669) during WL */ + if (dpde_flag) { + reg = reg_read(REG_DUNIT_CTRL_LOW_ADDR) | + (1 << REG_DUNIT_CTRL_LOW_DPDE_OFFS); + reg_write(REG_DUNIT_CTRL_LOW_ADDR, reg); + } +#endif + DEBUG_WL_S("DDR3 - Write Leveling - HW WL Ended Successfully\n"); + + /* If target freq = 400 move clock back */ + /* Write to control PUP to Control Deskew Regs */ + if (freq <= DDR_400) { + for (pup = 0; pup <= dram_info->num_of_total_pups; + pup++) { + ddr3_write_ctrl_pup_reg(1, pup, + CNTRL_PUP_DESKEW + pup, 0); + } + } + + return MV_OK; + } else { + /* Configure Each PUP with locked leveling settings */ + for (cs = 0; cs < MAX_CS; cs++) { + if (dram_info->cs_ena & (1 << cs)) { + for (pup = 0; + pup < dram_info->num_of_total_pups; + pup++) { + /* ECC support - bit 8 */ + pup_num = (pup == dram_info->num_of_std_pups) ? + ECC_BIT : pup; + ddr3_write_pup_reg(PUP_WL_MODE, cs, + pup_num, 0, 0); + } + } + } + + reg_write(REG_DRAM_TRAINING_ADDR, 0); + + /* If target freq = 400 move clock back */ + /* Write to control PUP to Control Deskew Regs */ + if (freq <= DDR_400) { + for (pup = 0; pup <= dram_info->num_of_total_pups; + pup++) { + ddr3_write_ctrl_pup_reg(1, pup, + CNTRL_PUP_DESKEW + pup, 0); + } + } + + DEBUG_WL_S("DDR3 - Write Leveling - HW WL Ended Successfully\n"); + return MV_NO_CHANGE; + } +} + +/* + * Name: ddr3_write_leveling_sw + * Desc: Execute Write leveling phase by SW + * Args: freq - current sequence frequency + * dram_info - main struct + * Notes: + * Returns: MV_OK if success, MV_FAIL if fail. + */ +int ddr3_write_leveling_sw(u32 freq, int ratio_2to1, MV_DRAM_INFO *dram_info) +{ + u32 reg, cs, cnt, pup, max_pup_num; + u32 res[MAX_CS]; + max_pup_num = dram_info->num_of_total_pups; + __maybe_unused int dpde_flag = 0; + + /* Debug message - Start Write leveling procedure */ + DEBUG_WL_S("DDR3 - Write Leveling - Starting SW WL procedure\n"); + +#ifdef MV88F67XX + /* Dynamic pad issue (BTS669) during WL */ + reg = reg_read(REG_DUNIT_CTRL_LOW_ADDR); + if (reg & (1 << REG_DUNIT_CTRL_LOW_DPDE_OFFS)) { + dpde_flag = 1; + reg_write(REG_DUNIT_CTRL_LOW_ADDR, + reg & ~(1 << REG_DUNIT_CTRL_LOW_DPDE_OFFS)); + } +#endif + + /* Set Output buffer-off to all CS and correct ODT values */ + for (cs = 0; cs < MAX_CS; cs++) { + if (dram_info->cs_ena & (1 << cs)) { + reg = reg_read(REG_DDR3_MR1_ADDR) & + REG_DDR3_MR1_ODT_MASK; + reg |= odt_static[dram_info->cs_ena][cs]; + reg |= (1 << REG_DDR3_MR1_OUTBUF_DIS_OFFS); + + /* 0x15D0 - DDR3 MR0 Register */ + reg_write(REG_DDR3_MR1_ADDR, reg); + /* Issue MRS Command to current cs */ + reg = REG_SDRAM_OPERATION_CMD_MR1 & + ~(1 << (REG_SDRAM_OPERATION_CS_OFFS + cs)); + /* + * [3-0] = 0x4 - MR1 Command, [11-8] - + * enable current cs + */ + /* 0x1418 - SDRAM Operation Register */ + reg_write(REG_SDRAM_OPERATION_ADDR, reg); + + udelay(MRS_DELAY); + } + } + + DEBUG_WL_FULL_S("DDR3 - Write Leveling - Qoff and RTT Values are set for all Cs\n"); + + /* Enable SW override */ + reg = reg_read(REG_DRAM_TRAINING_2_ADDR) | + (1 << REG_DRAM_TRAINING_2_SW_OVRD_OFFS); + /* [0] = 1 - Enable SW override */ + /* 0x15B8 - Training SW 2 Register */ + reg_write(REG_DRAM_TRAINING_2_ADDR, reg); + DEBUG_WL_FULL_S("DDR3 - Write Leveling - SW Override Enabled\n"); + + /* Enable PHY write leveling mode */ + reg = reg_read(REG_DRAM_TRAINING_2_ADDR) & + ~(1 << REG_DRAM_TRAINING_2_WL_MODE_OFFS); + /* [2] = 0 - TrnWLMode - Enable */ + /* 0x15B8 - Training SW 2 Register */ + reg_write(REG_DRAM_TRAINING_2_ADDR, reg); + /* Reset WL results arry */ + memset(dram_info->wl_val, 0, sizeof(u32) * MAX_CS * MAX_PUP_NUM * 7); + + /* Loop for each cs */ + for (cs = 0; cs < MAX_CS; cs++) { + if (dram_info->cs_ena & (1 << cs)) { + DEBUG_WL_FULL_C("DDR3 - Write Leveling - Starting working with Cs - ", + (u32) cs, 1); + /* Refresh X9 current cs */ + DEBUG_WL_FULL_S("DDR3 - Write Leveling - Refresh X9\n"); + for (cnt = 0; cnt < COUNT_WL_RFRS; cnt++) { + reg = + REG_SDRAM_OPERATION_CMD_RFRS & ~(1 << + (REG_SDRAM_OPERATION_CS_OFFS + + cs)); + /* [3-0] = 0x2 - refresh, [11-8] - enable current cs */ + reg_write(REG_SDRAM_OPERATION_ADDR, reg); /* 0x1418 - SDRAM Operation Register */ + + do { + reg = + ((reg_read + (REG_SDRAM_OPERATION_ADDR)) & + REG_SDRAM_OPERATION_CMD_RFRS_DONE); + } while (reg); /* Wait for '0' */ + } + + /* Configure MR1 in Cs[CsNum] - write leveling on, output buffer on */ + DEBUG_WL_FULL_S("DDR3 - Write Leveling - Configure MR1 for current Cs: WL-on,OB-on\n"); + reg = reg_read(REG_DDR3_MR1_ADDR) & + REG_DDR3_MR1_OUTBUF_WL_MASK; + /* Set ODT Values */ + reg &= REG_DDR3_MR1_ODT_MASK; + reg |= odt_static[dram_info->cs_ena][cs]; + /* Enable WL MODE */ + reg |= (1 << REG_DDR3_MR1_WL_ENA_OFFS); + /* [7]=1, [12]=0 - Output Buffer and write leveling enabled */ + reg_write(REG_DDR3_MR1_ADDR, reg); /* 0x15D4 - DDR3 MR1 Register */ + /* Issue MRS Command to current cs */ + reg = REG_SDRAM_OPERATION_CMD_MR1 & + ~(1 << (REG_SDRAM_OPERATION_CS_OFFS + cs)); + /* + * [3-0] = 0x4 - MR1 Command, [11-8] - + * enable current cs + */ + /* 0x1418 - SDRAM Operation Register */ + reg_write(REG_SDRAM_OPERATION_ADDR, reg); + + udelay(MRS_DELAY); + + /* Write leveling cs[cs] */ + if (MV_OK != + ddr3_write_leveling_single_cs(cs, freq, ratio_2to1, + (u32 *)(res + cs), + dram_info)) { + DEBUG_WL_FULL_C("DDR3 - Write Leveling single Cs - FAILED - Cs - ", + (u32) cs, 1); + for (pup = 0; pup < max_pup_num; pup++) { + if (((res[cs] >> pup) & 0x1) == 0) { + DEBUG_WL_C("Failed Byte : ", + pup, 1); + } + } + return MV_FAIL; + } + + /* Set TrnWLDeUpd - After each CS is done */ + reg = reg_read(REG_TRAINING_WL_ADDR) | + (1 << REG_TRAINING_WL_CS_DONE_OFFS); + /* 0x16AC - Training Write leveling register */ + reg_write(REG_TRAINING_WL_ADDR, reg); + + /* + * Debug message - Finished Write leveling cs[cs] - + * each PUP Fail/Success + */ + DEBUG_WL_FULL_C("DDR3 - Write Leveling - Finished Cs - ", (u32) cs, + 1); + DEBUG_WL_FULL_C("DDR3 - Write Leveling - The Results: 1-PUP locked, 0-PUP failed -", + (u32) res[cs], 3); + + /* + * Configure MR1 in cs[cs] - write leveling off (0), + * output buffer off (1) + */ + reg = reg_read(REG_DDR3_MR1_ADDR) & + REG_DDR3_MR1_OUTBUF_WL_MASK; + reg |= (1 << REG_DDR3_MR1_OUTBUF_DIS_OFFS); + /* No need to sort ODT since it is same CS */ + /* 0x15D4 - DDR3 MR1 Register */ + reg_write(REG_DDR3_MR1_ADDR, reg); + /* Issue MRS Command to current cs */ + reg = REG_SDRAM_OPERATION_CMD_MR1 & + ~(1 << (REG_SDRAM_OPERATION_CS_OFFS + cs)); + /* + * [3-0] = 0x4 - MR1 Command, [11-8] - + * enable current cs + */ + /* 0x1418 - SDRAM Operation Register */ + reg_write(REG_SDRAM_OPERATION_ADDR, reg); + + udelay(MRS_DELAY); + } + } + + /* Disable WL Mode */ + /* [2]=1 - TrnWLMode - Disable */ + reg = reg_read(REG_DRAM_TRAINING_2_ADDR); + reg |= (1 << REG_DRAM_TRAINING_2_WL_MODE_OFFS); + /* 0x15B8 - Training SW 2 Register */ + reg_write(REG_DRAM_TRAINING_2_ADDR, reg); + + /* Disable SW override - Must be in a different stage */ + /* [0]=0 - Enable SW override */ + reg = reg_read(REG_DRAM_TRAINING_2_ADDR); + reg &= ~(1 << REG_DRAM_TRAINING_2_SW_OVRD_OFFS); + /* 0x15B8 - Training SW 2 Register */ + reg_write(REG_DRAM_TRAINING_2_ADDR, reg); + + /* Set Output buffer-on to all CS and correct ODT values */ + for (cs = 0; cs < MAX_CS; cs++) { + if (dram_info->cs_ena & (1 << cs)) { + reg = reg_read(REG_DDR3_MR1_ADDR) & + REG_DDR3_MR1_ODT_MASK; + reg &= REG_DDR3_MR1_OUTBUF_WL_MASK; + reg |= odt_static[dram_info->cs_ena][cs]; + + /* 0x15D0 - DDR3 MR1 Register */ + reg_write(REG_DDR3_MR1_ADDR, reg); + /* Issue MRS Command to current cs */ + reg = REG_SDRAM_OPERATION_CMD_MR1 & + ~(1 << (REG_SDRAM_OPERATION_CS_OFFS + cs)); + /* + * [3-0] = 0x4 - MR1 Command, [11-8] - + * enable current cs + */ + /* 0x1418 - SDRAM Operation Register */ + reg_write(REG_SDRAM_OPERATION_ADDR, reg); + + udelay(MRS_DELAY); + } + } + +#ifdef MV88F67XX + /* Dynamic pad issue (BTS669) during WL */ + if (dpde_flag) { + reg = reg_read(REG_DUNIT_CTRL_LOW_ADDR) | + (1 << REG_DUNIT_CTRL_LOW_DPDE_OFFS); + reg_write(REG_DUNIT_CTRL_LOW_ADDR, reg); + } +#endif + DEBUG_WL_FULL_S("DDR3 - Write Leveling - Finished WL procedure for all Cs\n"); + + return MV_OK; +} + +#if !defined(MV88F672X) +/* + * Name: ddr3_write_leveling_sw + * Desc: Execute Write leveling phase by SW + * Args: freq - current sequence frequency + * dram_info - main struct + * Notes: + * Returns: MV_OK if success, MV_FAIL if fail. + */ +int ddr3_write_leveling_sw_reg_dimm(u32 freq, int ratio_2to1, + MV_DRAM_INFO *dram_info) +{ + u32 reg, cs, cnt, pup; + u32 res[MAX_CS]; + __maybe_unused int dpde_flag = 0; + + /* Debug message - Start Write leveling procedure */ + DEBUG_WL_S("DDR3 - Write Leveling - Starting SW WL procedure\n"); + +#ifdef MV88F67XX + /* Dynamic pad issue (BTS669) during WL */ + reg = reg_read(REG_DUNIT_CTRL_LOW_ADDR); + if (reg & (1 << REG_DUNIT_CTRL_LOW_DPDE_OFFS)) { + dpde_flag = 1; + reg_write(REG_DUNIT_CTRL_LOW_ADDR, + reg & ~(1 << REG_DUNIT_CTRL_LOW_DPDE_OFFS)); + } +#endif + + /* If target freq = 400 move clock start point */ + /* Write to control PUP to Control Deskew Regs */ + if (freq <= DDR_400) { + for (pup = 0; pup <= dram_info->num_of_total_pups; pup++) { + /* PUP_DELAY_MASK 0x1F */ + /* reg = 0x0C10001F + (uj << 16); */ + ddr3_write_ctrl_pup_reg(1, pup, CNTRL_PUP_DESKEW + pup, + 0x1F); + } + } + + /* Set Output buffer-off to all CS and correct ODT values */ + for (cs = 0; cs < MAX_CS; cs++) { + if (dram_info->cs_ena & (1 << cs)) { + reg = reg_read(REG_DDR3_MR1_ADDR) & + REG_DDR3_MR1_ODT_MASK; + reg |= odt_static[dram_info->cs_ena][cs]; + reg |= (1 << REG_DDR3_MR1_OUTBUF_DIS_OFFS); + + /* 0x15D0 - DDR3 MR0 Register */ + reg_write(REG_DDR3_MR1_ADDR, reg); + /* Issue MRS Command to current cs */ + reg = REG_SDRAM_OPERATION_CMD_MR1 & + ~(1 << (REG_SDRAM_OPERATION_CS_OFFS + cs)); + /* + * [3-0] = 0x4 - MR1 Command, [11-8] - + * enable current cs + */ + /* 0x1418 - SDRAM Operation Register */ + reg_write(REG_SDRAM_OPERATION_ADDR, reg); + + udelay(MRS_DELAY); + } + } + + DEBUG_WL_FULL_S("DDR3 - Write Leveling - Qoff and RTT Values are set for all Cs\n"); + + /* Enable SW override */ + reg = reg_read(REG_DRAM_TRAINING_2_ADDR) | + (1 << REG_DRAM_TRAINING_2_SW_OVRD_OFFS); + /* [0] = 1 - Enable SW override */ + /* 0x15B8 - Training SW 2 Register */ + reg_write(REG_DRAM_TRAINING_2_ADDR, reg); + DEBUG_WL_FULL_S("DDR3 - Write Leveling - SW Override Enabled\n"); + + /* Enable PHY write leveling mode */ + reg = reg_read(REG_DRAM_TRAINING_2_ADDR) & + ~(1 << REG_DRAM_TRAINING_2_WL_MODE_OFFS); + /* [2] = 0 - TrnWLMode - Enable */ + /* 0x15B8 - Training SW 2 Register */ + reg_write(REG_DRAM_TRAINING_2_ADDR, reg); + + /* Loop for each cs */ + for (cs = 0; cs < MAX_CS; cs++) { + if (dram_info->cs_ena & (1 << cs)) { + DEBUG_WL_FULL_C("DDR3 - Write Leveling - Starting working with Cs - ", + (u32) cs, 1); + + /* Refresh X9 current cs */ + DEBUG_WL_FULL_S("DDR3 - Write Leveling - Refresh X9\n"); + for (cnt = 0; cnt < COUNT_WL_RFRS; cnt++) { + reg = + REG_SDRAM_OPERATION_CMD_RFRS & ~(1 << + (REG_SDRAM_OPERATION_CS_OFFS + + cs)); + /* [3-0] = 0x2 - refresh, [11-8] - enable current cs */ + reg_write(REG_SDRAM_OPERATION_ADDR, reg); /* 0x1418 - SDRAM Operation Register */ + + do { + reg = + ((reg_read + (REG_SDRAM_OPERATION_ADDR)) & + REG_SDRAM_OPERATION_CMD_RFRS_DONE); + } while (reg); /* Wait for '0' */ + } + + /* + * Configure MR1 in Cs[CsNum] - write leveling on, + * output buffer on + */ + DEBUG_WL_FULL_S("DDR3 - Write Leveling - Configure MR1 for current Cs: WL-on,OB-on\n"); + reg = reg_read(REG_DDR3_MR1_ADDR) & + REG_DDR3_MR1_OUTBUF_WL_MASK; + /* Set ODT Values */ + reg &= REG_DDR3_MR1_ODT_MASK; + reg |= odt_static[dram_info->cs_ena][cs]; + /* Enable WL MODE */ + reg |= (1 << REG_DDR3_MR1_WL_ENA_OFFS); + /* + * [7]=1, [12]=0 - Output Buffer and write leveling + * enabled + */ + /* 0x15D4 - DDR3 MR1 Register */ + reg_write(REG_DDR3_MR1_ADDR, reg); + /* Issue MRS Command to current cs */ + reg = REG_SDRAM_OPERATION_CMD_MR1 & + ~(1 << (REG_SDRAM_OPERATION_CS_OFFS + cs)); + /* + * [3-0] = 0x4 - MR1 Command, [11-8] - + * enable current cs + */ + /* 0x1418 - SDRAM Operation Register */ + reg_write(REG_SDRAM_OPERATION_ADDR, reg); + + udelay(MRS_DELAY); + + /* Write leveling cs[cs] */ + if (MV_OK != + ddr3_write_leveling_single_cs(cs, freq, ratio_2to1, + (u32 *)(res + cs), + dram_info)) { + DEBUG_WL_FULL_C("DDR3 - Write Leveling single Cs - FAILED - Cs - ", + (u32) cs, 1); + return MV_FAIL; + } + + /* Set TrnWLDeUpd - After each CS is done */ + reg = reg_read(REG_TRAINING_WL_ADDR) | + (1 << REG_TRAINING_WL_CS_DONE_OFFS); + /* 0x16AC - Training Write leveling register */ + reg_write(REG_TRAINING_WL_ADDR, reg); + + /* + * Debug message - Finished Write leveling cs[cs] - + * each PUP Fail/Success + */ + DEBUG_WL_FULL_C("DDR3 - Write Leveling - Finished Cs - ", (u32) cs, + 1); + DEBUG_WL_FULL_C("DDR3 - Write Leveling - The Results: 1-PUP locked, 0-PUP failed -", + (u32) res[cs], 3); + + /* Configure MR1 in cs[cs] - write leveling off (0), output buffer off (1) */ + reg = reg_read(REG_DDR3_MR1_ADDR) & + REG_DDR3_MR1_OUTBUF_WL_MASK; + reg |= (1 << REG_DDR3_MR1_OUTBUF_DIS_OFFS); + /* No need to sort ODT since it is same CS */ + /* 0x15D4 - DDR3 MR1 Register */ + reg_write(REG_DDR3_MR1_ADDR, reg); + /* Issue MRS Command to current cs */ + reg = REG_SDRAM_OPERATION_CMD_MR1 & + ~(1 << (REG_SDRAM_OPERATION_CS_OFFS + cs)); + /* + * [3-0] = 0x4 - MR1 Command, [11-8] - + * enable current cs + */ + /* 0x1418 - SDRAM Operation Register */ + reg_write(REG_SDRAM_OPERATION_ADDR, reg); + + udelay(MRS_DELAY); + } + } + + /* Disable WL Mode */ + /* [2]=1 - TrnWLMode - Disable */ + reg = reg_read(REG_DRAM_TRAINING_2_ADDR); + reg |= (1 << REG_DRAM_TRAINING_2_WL_MODE_OFFS); + /* 0x15B8 - Training SW 2 Register */ + reg_write(REG_DRAM_TRAINING_2_ADDR, reg); + + /* Disable SW override - Must be in a different stage */ + /* [0]=0 - Enable SW override */ + reg = reg_read(REG_DRAM_TRAINING_2_ADDR); + reg &= ~(1 << REG_DRAM_TRAINING_2_SW_OVRD_OFFS); + /* 0x15B8 - Training SW 2 Register */ + reg_write(REG_DRAM_TRAINING_2_ADDR, reg); + + /* Set Output buffer-on to all CS and correct ODT values */ + for (cs = 0; cs < MAX_CS; cs++) { + if (dram_info->cs_ena & (1 << cs)) { + reg = reg_read(REG_DDR3_MR1_ADDR) & + REG_DDR3_MR1_ODT_MASK; + reg &= REG_DDR3_MR1_OUTBUF_WL_MASK; + reg |= odt_static[dram_info->cs_ena][cs]; + + /* 0x15D0 - DDR3 MR1 Register */ + reg_write(REG_DDR3_MR1_ADDR, reg); + /* Issue MRS Command to current cs */ + reg = REG_SDRAM_OPERATION_CMD_MR1 & + ~(1 << (REG_SDRAM_OPERATION_CS_OFFS + cs)); + /* + * [3-0] = 0x4 - MR1 Command, [11-8] - + * enable current cs + */ + /* 0x1418 - SDRAM Operation Register */ + reg_write(REG_SDRAM_OPERATION_ADDR, reg); + + udelay(MRS_DELAY); + } + } + +#ifdef MV88F67XX + /* Dynamic pad issue (BTS669) during WL */ + if (dpde_flag) { + reg = reg_read(REG_DUNIT_CTRL_LOW_ADDR) | + (1 << REG_DUNIT_CTRL_LOW_DPDE_OFFS); + reg_write(REG_DUNIT_CTRL_LOW_ADDR, reg); + } +#endif + + /* If target freq = 400 move clock back */ + /* Write to control PUP to Control Deskew Regs */ + if (freq <= DDR_400) { + for (pup = 0; pup <= dram_info->num_of_total_pups; pup++) { + ddr3_write_ctrl_pup_reg(1, pup, CNTRL_PUP_DESKEW + pup, + 0); + } + } + + DEBUG_WL_FULL_S("DDR3 - Write Leveling - Finished WL procedure for all Cs\n"); + return MV_OK; +} +#endif + +/* + * Name: ddr3_write_leveling_single_cs + * Desc: Execute Write leveling for single Chip select + * Args: cs - current chip select + * freq - current sequence frequency + * result - res array + * dram_info - main struct + * Notes: + * Returns: MV_OK if success, MV_FAIL if fail. + */ +static int ddr3_write_leveling_single_cs(u32 cs, u32 freq, int ratio_2to1, + u32 *result, MV_DRAM_INFO *dram_info) +{ + u32 reg, pup_num, delay, phase, phaseMax, max_pup_num, pup, + max_pup_mask; + + max_pup_num = dram_info->num_of_total_pups; + *result = 0; + u32 flag[MAX_PUP_NUM] = { 0 }; + + DEBUG_WL_FULL_C("DDR3 - Write Leveling Single Cs - WL for Cs - ", + (u32) cs, 1); + + switch (max_pup_num) { + case 2: + max_pup_mask = 0x3; + break; + case 4: + max_pup_mask = 0xf; + DEBUG_WL_C("max_pup_mask = ", max_pup_mask, 3); + break; + case 5: + max_pup_mask = 0x1f; + DEBUG_WL_C("max_pup_mask = ", max_pup_mask, 3); + break; + case 8: + max_pup_mask = 0xff; + DEBUG_WL_C("max_pup_mask = ", max_pup_mask, 3); + break; + case 9: + max_pup_mask = 0x1ff; + DEBUG_WL_C("max_pup_mask = ", max_pup_mask, 3); + break; + default: + DEBUG_WL_C("ddr3_write_leveling_single_cs wrong max_pup_num = ", + max_pup_num, 3); + return MV_FAIL; + } + + /* CS ODT Override */ + reg = reg_read(REG_SDRAM_ODT_CTRL_HIGH_ADDR) & + REG_SDRAM_ODT_CTRL_HIGH_OVRD_MASK; + reg |= (REG_SDRAM_ODT_CTRL_HIGH_OVRD_ENA << (2 * cs)); + /* Set 0x3 - Enable ODT on the curent cs and disable on other cs */ + /* 0x1498 - SDRAM ODT Control high */ + reg_write(REG_SDRAM_ODT_CTRL_HIGH_ADDR, reg); + + DEBUG_WL_FULL_S("DDR3 - Write Leveling Single Cs - ODT Asserted for current Cs\n"); + + /* tWLMRD Delay */ + /* Delay of minimum 40 Dram clock cycles - 20 Tclk cycles */ + udelay(1); + + /* [1:0] - current cs number */ + reg = (reg_read(REG_TRAINING_WL_ADDR) & REG_TRAINING_WL_CS_MASK) | cs; + reg |= (1 << REG_TRAINING_WL_UPD_OFFS); /* [2] - trnWLCsUpd */ + /* 0x16AC - Training Write leveling register */ + reg_write(REG_TRAINING_WL_ADDR, reg); + + /* Broadcast to all PUPs: Reset DQS phase, reset leveling delay */ + ddr3_write_pup_reg(PUP_WL_MODE, cs, PUP_BC, 0, 0); + + /* Seek Edge */ + DEBUG_WL_FULL_S("DDR3 - Write Leveling Single Cs - Seek Edge - Current Cs\n"); + + /* Drive DQS high for one cycle - All data PUPs */ + DEBUG_WL_FULL_S("DDR3 - Write Leveling Single Cs - Seek Edge - Driving DQS high for one cycle\n"); + if (!ratio_2to1) { + reg = (reg_read(REG_TRAINING_WL_ADDR) & + REG_TRAINING_WL_RATIO_MASK) | REG_TRAINING_WL_1TO1; + } else { + reg = (reg_read(REG_TRAINING_WL_ADDR) & + REG_TRAINING_WL_RATIO_MASK) | REG_TRAINING_WL_2TO1; + } + /* 0x16AC - Training Write leveling register */ + reg_write(REG_TRAINING_WL_ADDR, reg); + + /* Wait tWLdelay */ + do { + /* [29] - trnWLDelayExp */ + reg = (reg_read(REG_TRAINING_WL_ADDR)) & + REG_TRAINING_WL_DELAYEXP_MASK; + } while (reg == 0x0); /* Wait for '1' */ + + /* Read WL res */ + reg = (reg_read(REG_TRAINING_WL_ADDR) >> REG_TRAINING_WL_RESULTS_OFFS) & + REG_TRAINING_WL_RESULTS_MASK; + /* [28:20] - TrnWLResult */ + + if (!ratio_2to1) /* Different phase options for 2:1 or 1:1 modes */ + phaseMax = MAX_PHASE_1TO1; + else + phaseMax = MAX_PHASE_2TO1; + + DEBUG_WL_FULL_S("DDR3 - Write Leveling Single Cs - Seek Edge - Shift DQS + Octet Leveling\n"); + + /* Shift DQS + Octet leveling */ + for (phase = 0; phase < phaseMax; phase++) { + for (delay = 0; delay < MAX_DELAY; delay++) { + /* Broadcast to all PUPs: DQS phase,leveling delay */ + ddr3_write_pup_reg(PUP_WL_MODE, cs, PUP_BC, phase, + delay); + + udelay(1); /* Delay of 3 Tclk cycles */ + + DEBUG_WL_FULL_S("DDR3 - Write Leveling Single Cs - Seek Edge: Phase = "); + DEBUG_WL_FULL_D((u32) phase, 1); + DEBUG_WL_FULL_S(", Delay = "); + DEBUG_WL_FULL_D((u32) delay, 1); + DEBUG_WL_FULL_S(", Counter = "); + DEBUG_WL_FULL_D((u32) i, 1); + DEBUG_WL_FULL_S("\n"); + + /* Drive DQS high for one cycle - All data PUPs */ + if (!ratio_2to1) { + reg = (reg_read(REG_TRAINING_WL_ADDR) & + REG_TRAINING_WL_RATIO_MASK) | + REG_TRAINING_WL_1TO1; + } else { + reg = (reg_read(REG_TRAINING_WL_ADDR) & + REG_TRAINING_WL_RATIO_MASK) | + REG_TRAINING_WL_2TO1; + } + reg_write(REG_TRAINING_WL_ADDR, reg); /* 0x16AC */ + + /* Wait tWLdelay */ + do { + reg = (reg_read(REG_TRAINING_WL_ADDR)) & + REG_TRAINING_WL_DELAYEXP_MASK; + } while (reg == 0x0); /* [29] Wait for '1' */ + + /* Read WL res */ + reg = reg_read(REG_TRAINING_WL_ADDR); + reg = (reg >> REG_TRAINING_WL_RESULTS_OFFS) & + REG_TRAINING_WL_RESULTS_MASK; /* [28:20] */ + + DEBUG_WL_FULL_C("DDR3 - Write Leveling Single Cs - Seek Edge: Results = ", + (u32) reg, 3); + + /* Update State machine */ + for (pup = 0; pup < (max_pup_num); pup++) { + /* ECC support - bit 8 */ + pup_num = (pup == dram_info->num_of_std_pups) ? + ECC_BIT : pup; + if (dram_info->wl_val[cs][pup][S] == 0) { + /* Update phase to PUP */ + dram_info->wl_val[cs][pup][P] = phase; + /* Update delay to PUP */ + dram_info->wl_val[cs][pup][D] = delay; + } + + if (((reg >> pup_num) & 0x1) == 0) + flag[pup_num] = 1; + + if (((reg >> pup_num) & 0x1) + && (flag[pup_num] == 1) + && (dram_info->wl_val[cs][pup][S] == 0)) { + /* + * If the PUP is locked now and in last + * counter states + */ + /* Go to next state */ + dram_info->wl_val[cs][pup][S] = 1; + /* Set res */ + *result = *result | (1 << pup_num); + } + } + + /* If all locked - Break the loops - Finished */ + if (*result == max_pup_mask) { + phase = phaseMax; + delay = MAX_DELAY; + DEBUG_WL_S("DDR3 - Write Leveling Single Cs - Seek Edge: All Locked\n"); + } + } + } + + /* Debug message - Print res for cs[i]: cs,PUP,Phase,Delay */ + DEBUG_WL_C("DDR3 - Write Leveling - Results for CS - ", (u32) cs, 1); + for (pup = 0; pup < (max_pup_num); pup++) { + DEBUG_WL_S("DDR3 - Write Leveling - PUP: "); + DEBUG_WL_D((u32) pup, 1); + DEBUG_WL_S(", Phase: "); + DEBUG_WL_D((u32) dram_info->wl_val[cs][pup][P], 1); + DEBUG_WL_S(", Delay: "); + DEBUG_WL_D((u32) dram_info->wl_val[cs][pup][D], 2); + DEBUG_WL_S("\n"); + } + + /* Check if some not locked and return error */ + if (*result != max_pup_mask) { + DEBUG_WL_S("DDR3 - Write Leveling - ERROR - not all PUPS were locked\n"); + return MV_FAIL; + } + + /* Configure Each PUP with locked leveling settings */ + for (pup = 0; pup < (max_pup_num); pup++) { + /* ECC support - bit 8 */ + pup_num = (pup == dram_info->num_of_std_pups) ? ECC_BIT : pup; + phase = dram_info->wl_val[cs][pup][P]; + delay = dram_info->wl_val[cs][pup][D]; + ddr3_write_pup_reg(PUP_WL_MODE, cs, pup_num, phase, delay); + } + + /* CS ODT Override */ + reg = reg_read(REG_SDRAM_ODT_CTRL_HIGH_ADDR) & + REG_SDRAM_ODT_CTRL_HIGH_OVRD_MASK; + /* 0x1498 - SDRAM ODT Control high */ + reg_write(REG_SDRAM_ODT_CTRL_HIGH_ADDR, reg); + + return MV_OK; +} + +/* + * Perform DDR3 Control PUP Indirect Write + */ +static void ddr3_write_ctrl_pup_reg(int bc_acc, u32 pup, u32 reg_addr, u32 data) +{ + u32 reg = 0; + + /* Store value for write */ + reg = (data & 0xFFFF); + + /* Set bit 26 for control PHY access */ + reg |= (1 << REG_PHY_CNTRL_OFFS); + + /* Configure BC or UC access to PHYs */ + if (bc_acc == 1) + reg |= (1 << REG_PHY_BC_OFFS); + else + reg |= (pup << REG_PHY_PUP_OFFS); + + /* Set PHY register address to write to */ + reg |= (reg_addr << REG_PHY_CS_OFFS); + + reg_write(REG_PHY_REGISTRY_FILE_ACCESS_ADDR, reg); /* 0x16A0 */ + reg |= REG_PHY_REGISTRY_FILE_ACCESS_OP_WR; + reg_write(REG_PHY_REGISTRY_FILE_ACCESS_ADDR, reg); /* 0x16A0 */ + + do { + reg = (reg_read(REG_PHY_REGISTRY_FILE_ACCESS_ADDR)) & + REG_PHY_REGISTRY_FILE_ACCESS_OP_DONE; + } while (reg); /* Wait for '0' to mark the end of the transaction */ +} diff --git a/drivers/ddr/mvebu/xor.c b/drivers/ddr/mvebu/xor.c new file mode 100644 index 0000000..66c96ae --- /dev/null +++ b/drivers/ddr/mvebu/xor.c @@ -0,0 +1,436 @@ +/* + * Copyright (C) Marvell International Ltd. and its affiliates + * + * SPDX-License-Identifier: GPL-2.0 + */ + +#include <common.h> +#include <i2c.h> +#include <spl.h> +#include <asm/io.h> +#include <asm/arch/cpu.h> +#include <asm/arch/soc.h> + +#include "xor.h" +#include "xor_regs.h" + +static u32 xor_regs_ctrl_backup; +static u32 xor_regs_base_backup[MAX_CS]; +static u32 xor_regs_mask_backup[MAX_CS]; + +static void mv_xor_hal_init(u32 chan_num); +static int mv_xor_cmd_set(u32 chan, int command); +static int mv_xor_ctrl_set(u32 chan, u32 xor_ctrl); + +void mv_sys_xor_init(MV_DRAM_INFO *dram_info) +{ + u32 reg, ui, base, cs_count; + + xor_regs_ctrl_backup = reg_read(XOR_WINDOW_CTRL_REG(0, 0)); + for (ui = 0; ui < MAX_CS; ui++) + xor_regs_base_backup[ui] = reg_read(XOR_BASE_ADDR_REG(0, ui)); + for (ui = 0; ui < MAX_CS; ui++) + xor_regs_mask_backup[ui] = reg_read(XOR_SIZE_MASK_REG(0, ui)); + + reg = 0; + for (ui = 0; ui < (dram_info->num_cs + 1); ui++) { + /* Enable Window x for each CS */ + reg |= (0x1 << (ui)); + /* Enable Window x for each CS */ + reg |= (0x3 << ((ui * 2) + 16)); + } + + reg_write(XOR_WINDOW_CTRL_REG(0, 0), reg); + + /* Last window - Base - 0x40000000, Attribute 0x1E - SRAM */ + base = (SRAM_BASE & 0xFFFF0000) | 0x1E00; + reg_write(XOR_BASE_ADDR_REG(0, dram_info->num_cs), base); + /* Last window - Size - 64 MB */ + reg_write(XOR_SIZE_MASK_REG(0, dram_info->num_cs), 0x03FF0000); + + cs_count = 0; + for (ui = 0; ui < MAX_CS; ui++) { + if (dram_info->cs_ena & (1 << ui)) { + /* + * Window x - Base - 0x00000000, Attribute 0x0E - DRAM + */ + base = 0; + switch (ui) { + case 0: + base |= 0xE00; + break; + case 1: + base |= 0xD00; + break; + case 2: + base |= 0xB00; + break; + case 3: + base |= 0x700; + break; + } + + reg_write(XOR_BASE_ADDR_REG(0, cs_count), base); + + /* Window x - Size - 256 MB */ + reg_write(XOR_SIZE_MASK_REG(0, cs_count), 0x0FFF0000); + cs_count++; + } + } + + mv_xor_hal_init(1); + + return; +} + +void mv_sys_xor_finish(void) +{ + u32 ui; + + reg_write(XOR_WINDOW_CTRL_REG(0, 0), xor_regs_ctrl_backup); + for (ui = 0; ui < MAX_CS; ui++) + reg_write(XOR_BASE_ADDR_REG(0, ui), xor_regs_base_backup[ui]); + for (ui = 0; ui < MAX_CS; ui++) + reg_write(XOR_SIZE_MASK_REG(0, ui), xor_regs_mask_backup[ui]); + + reg_write(XOR_ADDR_OVRD_REG(0, 0), 0); +} + +/* + * mv_xor_hal_init - Initialize XOR engine + * + * DESCRIPTION: + * This function initialize XOR unit. + * INPUT: + * None. + * + * OUTPUT: + * None. + * + * RETURN: + * MV_BAD_PARAM if parameters to function invalid, MV_OK otherwise. + */ +static void mv_xor_hal_init(u32 chan_num) +{ + u32 i; + + /* Abort any XOR activity & set default configuration */ + for (i = 0; i < chan_num; i++) { + mv_xor_cmd_set(i, MV_STOP); + mv_xor_ctrl_set(i, (1 << XEXCR_REG_ACC_PROTECT_OFFS) | + (4 << XEXCR_DST_BURST_LIMIT_OFFS) | + (4 << XEXCR_SRC_BURST_LIMIT_OFFS)); + } +} + +/* + * mv_xor_ctrl_set - Set XOR channel control registers + * + * DESCRIPTION: + * + * INPUT: + * + * OUTPUT: + * None. + * + * RETURN: + * MV_BAD_PARAM if parameters to function invalid, MV_OK otherwise. + * NOTE: + * This function does not modify the OperationMode field of control register. + * + */ +static int mv_xor_ctrl_set(u32 chan, u32 xor_ctrl) +{ + u32 val; + + /* Update the XOR Engine [0..1] Configuration Registers (XExCR) */ + val = reg_read(XOR_CONFIG_REG(XOR_UNIT(chan), XOR_CHAN(chan))) + & XEXCR_OPERATION_MODE_MASK; + xor_ctrl &= ~XEXCR_OPERATION_MODE_MASK; + xor_ctrl |= val; + reg_write(XOR_CONFIG_REG(XOR_UNIT(chan), XOR_CHAN(chan)), xor_ctrl); + + return MV_OK; +} + +int mv_xor_mem_init(u32 chan, u32 start_ptr, u32 block_size, u32 init_val_high, + u32 init_val_low) +{ + u32 tmp; + + /* Parameter checking */ + if (chan >= MV_XOR_MAX_CHAN) + return MV_BAD_PARAM; + + if (MV_ACTIVE == mv_xor_state_get(chan)) + return MV_BUSY; + + if ((block_size < XEXBSR_BLOCK_SIZE_MIN_VALUE) || + (block_size > XEXBSR_BLOCK_SIZE_MAX_VALUE)) + return MV_BAD_PARAM; + + /* Set the operation mode to Memory Init */ + tmp = reg_read(XOR_CONFIG_REG(XOR_UNIT(chan), XOR_CHAN(chan))); + tmp &= ~XEXCR_OPERATION_MODE_MASK; + tmp |= XEXCR_OPERATION_MODE_MEM_INIT; + reg_write(XOR_CONFIG_REG(XOR_UNIT(chan), XOR_CHAN(chan)), tmp); + + /* + * Update the start_ptr field in XOR Engine [0..1] Destination Pointer + * Register (XExDPR0) + */ + reg_write(XOR_DST_PTR_REG(XOR_UNIT(chan), XOR_CHAN(chan)), start_ptr); + + /* + * Update the BlockSize field in the XOR Engine[0..1] Block Size + * Registers (XExBSR) + */ + reg_write(XOR_BLOCK_SIZE_REG(XOR_UNIT(chan), XOR_CHAN(chan)), + block_size); + + /* + * Update the field InitValL in the XOR Engine Initial Value Register + * Low (XEIVRL) + */ + reg_write(XOR_INIT_VAL_LOW_REG(XOR_UNIT(chan)), init_val_low); + + /* + * Update the field InitValH in the XOR Engine Initial Value Register + * High (XEIVRH) + */ + reg_write(XOR_INIT_VAL_HIGH_REG(XOR_UNIT(chan)), init_val_high); + + /* Start transfer */ + reg_bit_set(XOR_ACTIVATION_REG(XOR_UNIT(chan), XOR_CHAN(chan)), + XEXACTR_XESTART_MASK); + + return MV_OK; +} + +/* + * mv_xor_transfer - Transfer data from source to destination on one of + * three modes (XOR,CRC32,DMA) + * + * DESCRIPTION: + * This function initiates XOR channel, according to function parameters, + * in order to perform XOR or CRC32 or DMA transaction. + * To gain maximum performance the user is asked to keep the following + * restrictions: + * 1) Selected engine is available (not busy). + * 1) This module does not take into consideration CPU MMU issues. + * In order for the XOR engine to access the appropreate source + * and destination, address parameters must be given in system + * physical mode. + * 2) This API does not take care of cache coherency issues. The source, + * destination and in case of chain the descriptor list are assumed + * to be cache coherent. + * 4) Parameters validity. For example, does size parameter exceeds + * maximum byte count of descriptor mode (16M or 64K). + * + * INPUT: + * chan - XOR channel number. See MV_XOR_CHANNEL enumerator. + * xor_type - One of three: XOR, CRC32 and DMA operations. + * xor_chain_ptr - address of chain pointer + * + * OUTPUT: + * None. + * + * RETURS: + * MV_BAD_PARAM if parameters to function invalid, MV_OK otherwise. + * + */ +int mv_xor_transfer(u32 chan, int xor_type, u32 xor_chain_ptr) +{ + u32 tmp; + + /* Parameter checking */ + if (chan >= MV_XOR_MAX_CHAN) { + debug("%s: ERR. Invalid chan num %d\n", __func__, chan); + return MV_BAD_PARAM; + } + + if (MV_ACTIVE == mv_xor_state_get(chan)) { + debug("%s: ERR. Channel is already active\n", __func__); + return MV_BUSY; + } + + if (0x0 == xor_chain_ptr) { + debug("%s: ERR. xor_chain_ptr is NULL pointer\n", __func__); + return MV_BAD_PARAM; + } + + /* Read configuration register and mask the operation mode field */ + tmp = reg_read(XOR_CONFIG_REG(XOR_UNIT(chan), XOR_CHAN(chan))); + tmp &= ~XEXCR_OPERATION_MODE_MASK; + + switch (xor_type) { + case MV_XOR: + if (0 != (xor_chain_ptr & XEXDPR_DST_PTR_XOR_MASK)) { + debug("%s: ERR. Invalid chain pointer (bits [5:0] must be cleared)\n", + __func__); + return MV_BAD_PARAM; + } + + /* Set the operation mode to XOR */ + tmp |= XEXCR_OPERATION_MODE_XOR; + break; + + case MV_DMA: + if (0 != (xor_chain_ptr & XEXDPR_DST_PTR_DMA_MASK)) { + debug("%s: ERR. Invalid chain pointer (bits [4:0] must be cleared)\n", + __func__); + return MV_BAD_PARAM; + } + + /* Set the operation mode to DMA */ + tmp |= XEXCR_OPERATION_MODE_DMA; + break; + + case MV_CRC32: + if (0 != (xor_chain_ptr & XEXDPR_DST_PTR_CRC_MASK)) { + debug("%s: ERR. Invalid chain pointer (bits [4:0] must be cleared)\n", + __func__); + return MV_BAD_PARAM; + } + + /* Set the operation mode to CRC32 */ + tmp |= XEXCR_OPERATION_MODE_CRC; + break; + + default: + return MV_BAD_PARAM; + } + + /* Write the operation mode to the register */ + reg_write(XOR_CONFIG_REG(XOR_UNIT(chan), XOR_CHAN(chan)), tmp); + + /* + * Update the NextDescPtr field in the XOR Engine [0..1] Next Descriptor + * Pointer Register (XExNDPR) + */ + reg_write(XOR_NEXT_DESC_PTR_REG(XOR_UNIT(chan), XOR_CHAN(chan)), + xor_chain_ptr); + + /* Start transfer */ + reg_bit_set(XOR_ACTIVATION_REG(XOR_UNIT(chan), XOR_CHAN(chan)), + XEXACTR_XESTART_MASK); + + return MV_OK; +} + +/* + * mv_xor_state_get - Get XOR channel state. + * + * DESCRIPTION: + * XOR channel activity state can be active, idle, paused. + * This function retrunes the channel activity state. + * + * INPUT: + * chan - the channel number + * + * OUTPUT: + * None. + * + * RETURN: + * XOR_CHANNEL_IDLE - If the engine is idle. + * XOR_CHANNEL_ACTIVE - If the engine is busy. + * XOR_CHANNEL_PAUSED - If the engine is paused. + * MV_UNDEFINED_STATE - If the engine state is undefind or there is no + * such engine + * + */ +int mv_xor_state_get(u32 chan) +{ + u32 state; + + /* Parameter checking */ + if (chan >= MV_XOR_MAX_CHAN) { + debug("%s: ERR. Invalid chan num %d\n", __func__, chan); + return MV_UNDEFINED_STATE; + } + + /* Read the current state */ + state = reg_read(XOR_ACTIVATION_REG(XOR_UNIT(chan), XOR_CHAN(chan))); + state &= XEXACTR_XESTATUS_MASK; + + /* Return the state */ + switch (state) { + case XEXACTR_XESTATUS_IDLE: + return MV_IDLE; + case XEXACTR_XESTATUS_ACTIVE: + return MV_ACTIVE; + case XEXACTR_XESTATUS_PAUSED: + return MV_PAUSED; + } + + return MV_UNDEFINED_STATE; +} + +/* + * mv_xor_cmd_set - Set command of XOR channel + * + * DESCRIPTION: + * XOR channel can be started, idle, paused and restarted. + * Paused can be set only if channel is active. + * Start can be set only if channel is idle or paused. + * Restart can be set only if channel is paused. + * Stop can be set only if channel is active. + * + * INPUT: + * chan - The channel number + * command - The command type (start, stop, restart, pause) + * + * OUTPUT: + * None. + * + * RETURN: + * MV_OK on success , MV_BAD_PARAM on erroneous parameter, MV_ERROR on + * undefind XOR engine mode + * + */ +static int mv_xor_cmd_set(u32 chan, int command) +{ + int state; + + /* Parameter checking */ + if (chan >= MV_XOR_MAX_CHAN) { + debug("%s: ERR. Invalid chan num %d\n", __func__, chan); + return MV_BAD_PARAM; + } + + /* Get the current state */ + state = mv_xor_state_get(chan); + + /* Command is start and current state is idle */ + if ((command == MV_START) && (state == MV_IDLE)) { + reg_bit_set(XOR_ACTIVATION_REG(XOR_UNIT(chan), XOR_CHAN(chan)), + XEXACTR_XESTART_MASK); + return MV_OK; + } + /* Command is stop and current state is active */ + else if ((command == MV_STOP) && (state == MV_ACTIVE)) { + reg_bit_set(XOR_ACTIVATION_REG(XOR_UNIT(chan), XOR_CHAN(chan)), + XEXACTR_XESTOP_MASK); + return MV_OK; + } + /* Command is paused and current state is active */ + else if ((command == MV_PAUSED) && (state == MV_ACTIVE)) { + reg_bit_set(XOR_ACTIVATION_REG(XOR_UNIT(chan), XOR_CHAN(chan)), + XEXACTR_XEPAUSE_MASK); + return MV_OK; + } + /* Command is restart and current state is paused */ + else if ((command == MV_RESTART) && (state == MV_PAUSED)) { + reg_bit_set(XOR_ACTIVATION_REG(XOR_UNIT(chan), XOR_CHAN(chan)), + XEXACTR_XERESTART_MASK); + return MV_OK; + } + /* Command is stop and current state is active */ + else if ((command == MV_STOP) && (state == MV_IDLE)) + return MV_OK; + + /* Illegal command */ + debug("%s: ERR. Illegal command\n", __func__); + + return MV_BAD_PARAM; +} diff --git a/drivers/ddr/mvebu/xor.h b/drivers/ddr/mvebu/xor.h new file mode 100644 index 0000000..3536487 --- /dev/null +++ b/drivers/ddr/mvebu/xor.h @@ -0,0 +1,70 @@ +/* + * Copyright (C) Marvell International Ltd. and its affiliates + * + * SPDX-License-Identifier: GPL-2.0 + */ + +#ifndef __XOR_H +#define __XOR_H + +#include "ddr3_hw_training.h" + +#define MV_XOR_MAX_CHAN 4 /* total channels for all units together */ + +/* + * This enumerator describes the type of functionality the XOR channel + * can have while using the same data structures. + */ +enum xor_type { + MV_XOR, /* XOR channel functions as XOR accelerator */ + MV_DMA, /* XOR channel functions as IDMA channel */ + MV_CRC32 /* XOR channel functions as CRC 32 calculator */ +}; + +/* + * This enumerator describes the set of commands that can be applied on + * an engine (e.g. IDMA, XOR). Appling a comman depends on the current + * status (see MV_STATE enumerator) + * Start can be applied only when status is IDLE + * Stop can be applied only when status is IDLE, ACTIVE or PAUSED + * Pause can be applied only when status is ACTIVE + * Restart can be applied only when status is PAUSED + */ +enum mv_command { + MV_START, /* Start */ + MV_STOP, /* Stop */ + MV_PAUSE, /* Pause */ + MV_RESTART /* Restart */ +}; + +/* + * This enumerator describes the set of state conditions. + * Moving from one state to other is stricted. + */ +enum mv_state { + MV_IDLE, + MV_ACTIVE, + MV_PAUSED, + MV_UNDEFINED_STATE +}; + +/* XOR descriptor structure for CRC and DMA descriptor */ +struct crc_dma_desc { + u32 status; /* Successful descriptor execution indication */ + u32 crc32_result; /* Result of CRC-32 calculation */ + u32 desc_cmd; /* type of operation to be carried out on the data */ + u32 next_desc_ptr; /* Next descriptor address pointer */ + u32 byte_cnt; /* Size of source block part represented by the descriptor */ + u32 dst_addr; /* Destination Block address pointer (not used in CRC32 */ + u32 src_addr0; /* Mode: Source Block address pointer */ + u32 src_addr1; /* Mode: Source Block address pointer */ +} __packed; + +int mv_xor_state_get(u32 chan); +void mv_sys_xor_init(MV_DRAM_INFO *dram_info); +void mv_sys_xor_finish(void); +int mv_xor_transfer(u32 chan, int xor_type, u32 xor_chain_ptr); +int mv_xor_mem_init(u32 chan, u32 start_ptr, u32 block_size, u32 init_val_high, + u32 init_val_low); + +#endif /* __XOR_H */ diff --git a/drivers/ddr/mvebu/xor_regs.h b/drivers/ddr/mvebu/xor_regs.h new file mode 100644 index 0000000..884aa15 --- /dev/null +++ b/drivers/ddr/mvebu/xor_regs.h @@ -0,0 +1,103 @@ +/* + * Copyright (C) Marvell International Ltd. and its affiliates + * + * SPDX-License-Identifier: GPL-2.0 + */ + +#ifndef __XOR_REGS_H +#define __XOR_REGS_H + +/* + * For controllers that have two XOR units, then chans 2 & 3 will be mapped + * to channels 0 & 1 of unit 1 + */ +#define XOR_UNIT(chan) ((chan) >> 1) +#define XOR_CHAN(chan) ((chan) & 1) + +#define MV_XOR_REGS_OFFSET(unit) (0x60900) +#define MV_XOR_REGS_BASE(unit) (MV_XOR_REGS_OFFSET(unit)) + +/* XOR Engine Control Register Map */ +#define XOR_CHANNEL_ARBITER_REG(unit) (MV_XOR_REGS_BASE(unit)) +#define XOR_CONFIG_REG(unit, chan) (MV_XOR_REGS_BASE(unit) + (0x10 + ((chan) * 4))) +#define XOR_ACTIVATION_REG(unit, chan) (MV_XOR_REGS_BASE(unit) + (0x20 + ((chan) * 4))) + +/* XOR Engine Interrupt Register Map */ +#define XOR_CAUSE_REG(unit) (MV_XOR_REGS_BASE(unit) + 0x30) +#define XOR_MASK_REG(unit) (MV_XOR_REGS_BASE(unit) + 0x40) +#define XOR_ERROR_CAUSE_REG(unit) (MV_XOR_REGS_BASE(unit) + 0x50) +#define XOR_ERROR_ADDR_REG(unit) (MV_XOR_REGS_BASE(unit) + 0x60) + +/* XOR Engine Descriptor Register Map */ +#define XOR_NEXT_DESC_PTR_REG(unit, chan) (MV_XOR_REGS_BASE(unit) + (0x200 + ((chan) * 4))) +#define XOR_CURR_DESC_PTR_REG(unit, chan) (MV_XOR_REGS_BASE(unit) + (0x210 + ((chan) * 4))) +#define XOR_BYTE_COUNT_REG(unit, chan) (MV_XOR_REGS_BASE(unit) + (0x220 + ((chan) * 4))) + +#define XOR_DST_PTR_REG(unit, chan) (MV_XOR_REGS_BASE(unit) + (0x2B0 + ((chan) * 4))) +#define XOR_BLOCK_SIZE_REG(unit, chan) (MV_XOR_REGS_BASE(unit) + (0x2C0 + ((chan) * 4))) +#define XOR_TIMER_MODE_CTRL_REG(unit) (MV_XOR_REGS_BASE(unit) + 0x2D0) +#define XOR_TIMER_MODE_INIT_VAL_REG(unit) (MV_XOR_REGS_BASE(unit) + 0x2D4) +#define XOR_TIMER_MODE_CURR_VAL_REG(unit) (MV_XOR_REGS_BASE(unit) + 0x2D8) +#define XOR_INIT_VAL_LOW_REG(unit) (MV_XOR_REGS_BASE(unit) + 0x2E0) +#define XOR_INIT_VAL_HIGH_REG(unit) (MV_XOR_REGS_BASE(unit) + 0x2E4) + +/* XOR register fileds */ + +/* XOR Engine [0..1] Configuration Registers (XExCR) */ +#define XEXCR_OPERATION_MODE_OFFS (0) +#define XEXCR_OPERATION_MODE_MASK (7 << XEXCR_OPERATION_MODE_OFFS) +#define XEXCR_OPERATION_MODE_XOR (0 << XEXCR_OPERATION_MODE_OFFS) +#define XEXCR_OPERATION_MODE_CRC (1 << XEXCR_OPERATION_MODE_OFFS) +#define XEXCR_OPERATION_MODE_DMA (2 << XEXCR_OPERATION_MODE_OFFS) +#define XEXCR_OPERATION_MODE_ECC (3 << XEXCR_OPERATION_MODE_OFFS) +#define XEXCR_OPERATION_MODE_MEM_INIT (4 << XEXCR_OPERATION_MODE_OFFS) + +#define XEXCR_SRC_BURST_LIMIT_OFFS (4) +#define XEXCR_SRC_BURST_LIMIT_MASK (7 << XEXCR_SRC_BURST_LIMIT_OFFS) +#define XEXCR_DST_BURST_LIMIT_OFFS (8) +#define XEXCR_DST_BURST_LIMIT_MASK (7 << XEXCR_DST_BURST_LIMIT_OFFS) +#define XEXCR_DRD_RES_SWP_OFFS (12) +#define XEXCR_DRD_RES_SWP_MASK (1 << XEXCR_DRD_RES_SWP_OFFS) +#define XEXCR_DWR_REQ_SWP_OFFS (13) +#define XEXCR_DWR_REQ_SWP_MASK (1 << XEXCR_DWR_REQ_SWP_OFFS) +#define XEXCR_DES_SWP_OFFS (14) +#define XEXCR_DES_SWP_MASK (1 << XEXCR_DES_SWP_OFFS) +#define XEXCR_REG_ACC_PROTECT_OFFS (15) +#define XEXCR_REG_ACC_PROTECT_MASK (1 << XEXCR_REG_ACC_PROTECT_OFFS) + +/* XOR Engine [0..1] Activation Registers (XExACTR) */ +#define XEXACTR_XESTART_OFFS (0) +#define XEXACTR_XESTART_MASK (1 << XEXACTR_XESTART_OFFS) +#define XEXACTR_XESTOP_OFFS (1) +#define XEXACTR_XESTOP_MASK (1 << XEXACTR_XESTOP_OFFS) +#define XEXACTR_XEPAUSE_OFFS (2) +#define XEXACTR_XEPAUSE_MASK (1 << XEXACTR_XEPAUSE_OFFS) +#define XEXACTR_XERESTART_OFFS (3) +#define XEXACTR_XERESTART_MASK (1 << XEXACTR_XERESTART_OFFS) +#define XEXACTR_XESTATUS_OFFS (4) +#define XEXACTR_XESTATUS_MASK (3 << XEXACTR_XESTATUS_OFFS) +#define XEXACTR_XESTATUS_IDLE (0 << XEXACTR_XESTATUS_OFFS) +#define XEXACTR_XESTATUS_ACTIVE (1 << XEXACTR_XESTATUS_OFFS) +#define XEXACTR_XESTATUS_PAUSED (2 << XEXACTR_XESTATUS_OFFS) + +/* XOR Engine [0..1] Destination Pointer Register (XExDPR0) */ +#define XEXDPR_DST_PTR_OFFS (0) +#define XEXDPR_DST_PTR_MASK (0xFFFFFFFF << XEXDPR_DST_PTR_OFFS) +#define XEXDPR_DST_PTR_XOR_MASK (0x3F) +#define XEXDPR_DST_PTR_DMA_MASK (0x1F) +#define XEXDPR_DST_PTR_CRC_MASK (0x1F) + +/* XOR Engine[0..1] Block Size Registers (XExBSR) */ +#define XEXBSR_BLOCK_SIZE_OFFS (0) +#define XEXBSR_BLOCK_SIZE_MASK (0xFFFFFFFF << XEXBSR_BLOCK_SIZE_OFFS) +#define XEXBSR_BLOCK_SIZE_MIN_VALUE (128) +#define XEXBSR_BLOCK_SIZE_MAX_VALUE (0xFFFFFFFF) + +/* XOR Engine Address Decoding Register Map */ +#define XOR_WINDOW_CTRL_REG(unit, chan) (MV_XOR_REGS_BASE(unit) + (0x240 + ((chan) * 4))) +#define XOR_BASE_ADDR_REG(unit, win) (MV_XOR_REGS_BASE(unit) + (0x250 + ((win) * 4))) +#define XOR_SIZE_MASK_REG(unit, win) (MV_XOR_REGS_BASE(unit) + (0x270 + ((win) * 4))) +#define XOR_HIGH_ADDR_REMAP_REG(unit, win) (MV_XOR_REGS_BASE(unit) + (0x290 + ((win) * 4))) +#define XOR_ADDR_OVRD_REG(unit, win) (MV_XOR_REGS_BASE(unit) + (0x2A0 + ((win) * 4))) + +#endif /* __XOR_REGS_H */ |