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

1312 lines
37 KiB
C
Raw Normal View History

// SPDX-License-Identifier: GPL-2.0
arm: mvebu: drivers/ddr: Add DDR3 driver with training code from Marvell bin_hdr This patch adds the DDR3 setup and training code taken from the Marvell U-Boot repository. This code used to be included as a binary (bin_hdr) into the AXP boot image. Not linked with the main U-Boot. With this code addition and the following serdes/PHY setup code, the Armada-XP support in mainline U-Boot is finally self-contained. So the complete image for booting can be built from mainline U-Boot. Without any additional external inclusion. Hopefully other MVEBU SoC's will follow here. Support for some SoC's has been removed in this version. This is: MV_MSYS: The code referred to by the MV_MSYS define is currently unused. And its not really planned to support this in mainline. So lets remove it to make the code clearer and increase the readability. MV88F68XX (A38x): The code referred to by the MV88F68XX define (A38x) is currently unused. And its partial and not sufficient for this device in this stage. So lets remove it to make the code clearer and increase the readability. MV88F66XX (ALP): The code referred to by the MV88F66XX define is currently unused. And its not really planned to support this in mainline. So lets remove it to make the code clearer and increase the readability. MV88F78X60_Z1: The code referred to by the MV88F78X60_Z1 define is currently unused. As the Z1 revision of the AXP is not supported in mainline anymore. So lets remove it to make the code clearer and increase the readability. Remove support for Z1 & A0 AXP revisions (steppings). The current stepping is B0 and this is the only one that is actively supported in this code version. Tested on AXP using a SPD DIMM setup on the Marvell DB-MV784MP-GP board and on a custom fixed DDR configuration board (maxbcm). Note: This code has undergone many hours of coding-style cleanup and refactoring. It still is not checkpatch clean though, I'm afraid. As the factoring of the code has so many levels of indentation that many lines are longer than 80 chars. This might be some task to tackly later on. Signed-off-by: Stefan Roese <sr@denx.de> Reviewed-by: Luka Perkov <luka.perkov@sartura.hr>
2015-01-19 10:33:40 +00:00
/*
* Copyright (C) Marvell International Ltd. and its affiliates
*/
#include <common.h>
#include <i2c.h>
#include <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
arm: mvebu: drivers/ddr: Add DDR3 driver with training code from Marvell bin_hdr This patch adds the DDR3 setup and training code taken from the Marvell U-Boot repository. This code used to be included as a binary (bin_hdr) into the AXP boot image. Not linked with the main U-Boot. With this code addition and the following serdes/PHY setup code, the Armada-XP support in mainline U-Boot is finally self-contained. So the complete image for booting can be built from mainline U-Boot. Without any additional external inclusion. Hopefully other MVEBU SoC's will follow here. Support for some SoC's has been removed in this version. This is: MV_MSYS: The code referred to by the MV_MSYS define is currently unused. And its not really planned to support this in mainline. So lets remove it to make the code clearer and increase the readability. MV88F68XX (A38x): The code referred to by the MV88F68XX define (A38x) is currently unused. And its partial and not sufficient for this device in this stage. So lets remove it to make the code clearer and increase the readability. MV88F66XX (ALP): The code referred to by the MV88F66XX define is currently unused. And its not really planned to support this in mainline. So lets remove it to make the code clearer and increase the readability. MV88F78X60_Z1: The code referred to by the MV88F78X60_Z1 define is currently unused. As the Z1 revision of the AXP is not supported in mainline anymore. So lets remove it to make the code clearer and increase the readability. Remove support for Z1 & A0 AXP revisions (steppings). The current stepping is B0 and this is the only one that is actively supported in this code version. Tested on AXP using a SPD DIMM setup on the Marvell DB-MV784MP-GP board and on a custom fixed DDR configuration board (maxbcm). Note: This code has undergone many hours of coding-style cleanup and refactoring. It still is not checkpatch clean though, I'm afraid. As the factoring of the code has so many levels of indentation that many lines are longer than 80 chars. This might be some task to tackly later on. Signed-off-by: Stefan Roese <sr@denx.de> Reviewed-by: Luka Perkov <luka.perkov@sartura.hr>
2015-01-19 10:33:40 +00:00
#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--) {
struct udevice *udev;
arm: mvebu: drivers/ddr: Add DDR3 driver with training code from Marvell bin_hdr This patch adds the DDR3 setup and training code taken from the Marvell U-Boot repository. This code used to be included as a binary (bin_hdr) into the AXP boot image. Not linked with the main U-Boot. With this code addition and the following serdes/PHY setup code, the Armada-XP support in mainline U-Boot is finally self-contained. So the complete image for booting can be built from mainline U-Boot. Without any additional external inclusion. Hopefully other MVEBU SoC's will follow here. Support for some SoC's has been removed in this version. This is: MV_MSYS: The code referred to by the MV_MSYS define is currently unused. And its not really planned to support this in mainline. So lets remove it to make the code clearer and increase the readability. MV88F68XX (A38x): The code referred to by the MV88F68XX define (A38x) is currently unused. And its partial and not sufficient for this device in this stage. So lets remove it to make the code clearer and increase the readability. MV88F66XX (ALP): The code referred to by the MV88F66XX define is currently unused. And its not really planned to support this in mainline. So lets remove it to make the code clearer and increase the readability. MV88F78X60_Z1: The code referred to by the MV88F78X60_Z1 define is currently unused. As the Z1 revision of the AXP is not supported in mainline anymore. So lets remove it to make the code clearer and increase the readability. Remove support for Z1 & A0 AXP revisions (steppings). The current stepping is B0 and this is the only one that is actively supported in this code version. Tested on AXP using a SPD DIMM setup on the Marvell DB-MV784MP-GP board and on a custom fixed DDR configuration board (maxbcm). Note: This code has undergone many hours of coding-style cleanup and refactoring. It still is not checkpatch clean though, I'm afraid. As the factoring of the code has so many levels of indentation that many lines are longer than 80 chars. This might be some task to tackly later on. Signed-off-by: Stefan Roese <sr@denx.de> Reviewed-by: Luka Perkov <luka.perkov@sartura.hr>
2015-01-19 10:33:40 +00:00
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_get_chip_for_busnum(0, dimm_cur_addr, 1, &udev);
if (ret)
continue;
ret = dm_i2c_read(udev, 0, data, 3);
arm: mvebu: drivers/ddr: Add DDR3 driver with training code from Marvell bin_hdr This patch adds the DDR3 setup and training code taken from the Marvell U-Boot repository. This code used to be included as a binary (bin_hdr) into the AXP boot image. Not linked with the main U-Boot. With this code addition and the following serdes/PHY setup code, the Armada-XP support in mainline U-Boot is finally self-contained. So the complete image for booting can be built from mainline U-Boot. Without any additional external inclusion. Hopefully other MVEBU SoC's will follow here. Support for some SoC's has been removed in this version. This is: MV_MSYS: The code referred to by the MV_MSYS define is currently unused. And its not really planned to support this in mainline. So lets remove it to make the code clearer and increase the readability. MV88F68XX (A38x): The code referred to by the MV88F68XX define (A38x) is currently unused. And its partial and not sufficient for this device in this stage. So lets remove it to make the code clearer and increase the readability. MV88F66XX (ALP): The code referred to by the MV88F66XX define is currently unused. And its not really planned to support this in mainline. So lets remove it to make the code clearer and increase the readability. MV88F78X60_Z1: The code referred to by the MV88F78X60_Z1 define is currently unused. As the Z1 revision of the AXP is not supported in mainline anymore. So lets remove it to make the code clearer and increase the readability. Remove support for Z1 & A0 AXP revisions (steppings). The current stepping is B0 and this is the only one that is actively supported in this code version. Tested on AXP using a SPD DIMM setup on the Marvell DB-MV784MP-GP board and on a custom fixed DDR configuration board (maxbcm). Note: This code has undergone many hours of coding-style cleanup and refactoring. It still is not checkpatch clean though, I'm afraid. As the factoring of the code has so many levels of indentation that many lines are longer than 80 chars. This might be some task to tackly later on. Signed-off-by: Stefan Roese <sr@denx.de> Reviewed-by: Luka Perkov <luka.perkov@sartura.hr>
2015-01-19 10:33:40 +00:00
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) {
struct udevice *udev;
arm: mvebu: drivers/ddr: Add DDR3 driver with training code from Marvell bin_hdr This patch adds the DDR3 setup and training code taken from the Marvell U-Boot repository. This code used to be included as a binary (bin_hdr) into the AXP boot image. Not linked with the main U-Boot. With this code addition and the following serdes/PHY setup code, the Armada-XP support in mainline U-Boot is finally self-contained. So the complete image for booting can be built from mainline U-Boot. Without any additional external inclusion. Hopefully other MVEBU SoC's will follow here. Support for some SoC's has been removed in this version. This is: MV_MSYS: The code referred to by the MV_MSYS define is currently unused. And its not really planned to support this in mainline. So lets remove it to make the code clearer and increase the readability. MV88F68XX (A38x): The code referred to by the MV88F68XX define (A38x) is currently unused. And its partial and not sufficient for this device in this stage. So lets remove it to make the code clearer and increase the readability. MV88F66XX (ALP): The code referred to by the MV88F66XX define is currently unused. And its not really planned to support this in mainline. So lets remove it to make the code clearer and increase the readability. MV88F78X60_Z1: The code referred to by the MV88F78X60_Z1 define is currently unused. As the Z1 revision of the AXP is not supported in mainline anymore. So lets remove it to make the code clearer and increase the readability. Remove support for Z1 & A0 AXP revisions (steppings). The current stepping is B0 and this is the only one that is actively supported in this code version. Tested on AXP using a SPD DIMM setup on the Marvell DB-MV784MP-GP board and on a custom fixed DDR configuration board (maxbcm). Note: This code has undergone many hours of coding-style cleanup and refactoring. It still is not checkpatch clean though, I'm afraid. As the factoring of the code has so many levels of indentation that many lines are longer than 80 chars. This might be some task to tackly later on. Signed-off-by: Stefan Roese <sr@denx.de> Reviewed-by: Luka Perkov <luka.perkov@sartura.hr>
2015-01-19 10:33:40 +00:00
memset(spd_data, 0, SPD_SIZE * sizeof(u8));
ret = i2c_get_chip_for_busnum(0, dimm_addr, 1, &udev);
if (ret)
return MV_DDR3_TRAINING_ERR_TWSI_FAIL;
ret = dm_i2c_read(udev, 0, spd_data, SPD_SIZE);
arm: mvebu: drivers/ddr: Add DDR3 driver with training code from Marvell bin_hdr This patch adds the DDR3 setup and training code taken from the Marvell U-Boot repository. This code used to be included as a binary (bin_hdr) into the AXP boot image. Not linked with the main U-Boot. With this code addition and the following serdes/PHY setup code, the Armada-XP support in mainline U-Boot is finally self-contained. So the complete image for booting can be built from mainline U-Boot. Without any additional external inclusion. Hopefully other MVEBU SoC's will follow here. Support for some SoC's has been removed in this version. This is: MV_MSYS: The code referred to by the MV_MSYS define is currently unused. And its not really planned to support this in mainline. So lets remove it to make the code clearer and increase the readability. MV88F68XX (A38x): The code referred to by the MV88F68XX define (A38x) is currently unused. And its partial and not sufficient for this device in this stage. So lets remove it to make the code clearer and increase the readability. MV88F66XX (ALP): The code referred to by the MV88F66XX define is currently unused. And its not really planned to support this in mainline. So lets remove it to make the code clearer and increase the readability. MV88F78X60_Z1: The code referred to by the MV88F78X60_Z1 define is currently unused. As the Z1 revision of the AXP is not supported in mainline anymore. So lets remove it to make the code clearer and increase the readability. Remove support for Z1 & A0 AXP revisions (steppings). The current stepping is B0 and this is the only one that is actively supported in this code version. Tested on AXP using a SPD DIMM setup on the Marvell DB-MV784MP-GP board and on a custom fixed DDR configuration board (maxbcm). Note: This code has undergone many hours of coding-style cleanup and refactoring. It still is not checkpatch clean though, I'm afraid. As the factoring of the code has so many levels of indentation that many lines are longer than 80 chars. This might be some task to tackly later on. Signed-off-by: Stefan Roese <sr@denx.de> Reviewed-by: Luka Perkov <luka.perkov@sartura.hr>
2015-01-19 10:33:40 +00:00
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