2018-05-06 21:58:06 +00:00
|
|
|
// SPDX-License-Identifier: GPL-2.0
|
2008-08-26 20:01:29 +00:00
|
|
|
/*
|
2018-01-29 17:44:35 +00:00
|
|
|
* Copyright 2008-2016 Freescale Semiconductor, Inc.
|
2021-10-10 16:12:16 +00:00
|
|
|
* Copyright 2017-2021 NXP Semiconductor
|
2008-08-26 20:01:29 +00:00
|
|
|
*/
|
|
|
|
|
|
|
|
#include <common.h>
|
2013-09-30 16:22:09 +00:00
|
|
|
#include <fsl_ddr_sdram.h>
|
2020-05-10 17:40:05 +00:00
|
|
|
#include <log.h>
|
2020-05-10 17:40:13 +00:00
|
|
|
#include <asm/bitops.h>
|
2008-08-26 20:01:29 +00:00
|
|
|
|
2013-09-30 16:22:09 +00:00
|
|
|
#include <fsl_ddr.h>
|
2008-08-26 20:01:29 +00:00
|
|
|
|
2014-03-28 00:54:47 +00:00
|
|
|
#if defined(CONFIG_SYS_FSL_DDR3) || defined(CONFIG_SYS_FSL_DDR4)
|
2012-10-29 13:34:37 +00:00
|
|
|
static unsigned int
|
2015-01-06 21:18:50 +00:00
|
|
|
compute_cas_latency(const unsigned int ctrl_num,
|
|
|
|
const dimm_params_t *dimm_params,
|
2014-03-28 00:54:47 +00:00
|
|
|
common_timing_params_t *outpdimm,
|
|
|
|
unsigned int number_of_dimms)
|
2009-03-14 04:48:30 +00:00
|
|
|
{
|
|
|
|
unsigned int i;
|
|
|
|
unsigned int common_caslat;
|
|
|
|
unsigned int caslat_actual;
|
|
|
|
unsigned int retry = 16;
|
2015-03-19 16:30:27 +00:00
|
|
|
unsigned int tmp = ~0;
|
2021-10-10 16:12:16 +00:00
|
|
|
unsigned int mclk_ps = get_memory_clk_period_ps(ctrl_num);
|
2014-03-28 00:54:47 +00:00
|
|
|
#ifdef CONFIG_SYS_FSL_DDR3
|
|
|
|
const unsigned int taamax = 20000;
|
|
|
|
#else
|
|
|
|
const unsigned int taamax = 18000;
|
|
|
|
#endif
|
2009-03-14 04:48:30 +00:00
|
|
|
|
|
|
|
/* compute the common CAS latency supported between slots */
|
2015-03-19 16:30:27 +00:00
|
|
|
for (i = 0; i < number_of_dimms; i++) {
|
2012-08-17 08:22:41 +00:00
|
|
|
if (dimm_params[i].n_ranks)
|
2013-09-25 05:11:19 +00:00
|
|
|
tmp &= dimm_params[i].caslat_x;
|
2012-08-17 08:22:41 +00:00
|
|
|
}
|
2009-03-14 04:48:30 +00:00
|
|
|
common_caslat = tmp;
|
|
|
|
|
2021-10-10 16:12:16 +00:00
|
|
|
if (!mclk_ps) {
|
|
|
|
printf("DDR clock (MCLK cycle was 0 ps), So setting it to slowest DIMM(s) (tCKmin %u ps).\n",
|
|
|
|
outpdimm->tckmin_x_ps);
|
|
|
|
mclk_ps = outpdimm->tckmin_x_ps;
|
|
|
|
}
|
|
|
|
|
2009-03-14 04:48:30 +00:00
|
|
|
/* validate if the memory clk is in the range of dimms */
|
2014-03-28 00:54:47 +00:00
|
|
|
if (mclk_ps < outpdimm->tckmin_x_ps) {
|
2011-05-05 23:14:14 +00:00
|
|
|
printf("DDR clock (MCLK cycle %u ps) is faster than "
|
|
|
|
"the slowest DIMM(s) (tCKmin %u ps) can support.\n",
|
2014-03-28 00:54:47 +00:00
|
|
|
mclk_ps, outpdimm->tckmin_x_ps);
|
|
|
|
}
|
|
|
|
#ifdef CONFIG_SYS_FSL_DDR4
|
|
|
|
if (mclk_ps > outpdimm->tckmax_ps) {
|
|
|
|
printf("DDR clock (MCLK cycle %u ps) is slower than DIMM(s) (tCKmax %u ps) can support.\n",
|
|
|
|
mclk_ps, outpdimm->tckmax_ps);
|
2009-03-14 04:48:30 +00:00
|
|
|
}
|
2014-03-28 00:54:47 +00:00
|
|
|
#endif
|
2009-03-14 04:48:30 +00:00
|
|
|
/* determine the acutal cas latency */
|
2014-03-28 00:54:47 +00:00
|
|
|
caslat_actual = (outpdimm->taamin_ps + mclk_ps - 1) / mclk_ps;
|
2009-03-14 04:48:30 +00:00
|
|
|
/* check if the dimms support the CAS latency */
|
|
|
|
while (!(common_caslat & (1 << caslat_actual)) && retry > 0) {
|
|
|
|
caslat_actual++;
|
|
|
|
retry--;
|
|
|
|
}
|
|
|
|
/* once the caculation of caslat_actual is completed
|
|
|
|
* we must verify that this CAS latency value does not
|
2014-03-28 00:54:47 +00:00
|
|
|
* exceed tAAmax, which is 20 ns for all DDR3 speed grades,
|
|
|
|
* 18ns for all DDR4 speed grades.
|
2009-03-14 04:48:30 +00:00
|
|
|
*/
|
2014-03-28 00:54:47 +00:00
|
|
|
if (caslat_actual * mclk_ps > taamax) {
|
2016-03-17 14:44:47 +00:00
|
|
|
printf("The chosen cas latency %d is too large\n",
|
|
|
|
caslat_actual);
|
2009-03-14 04:48:30 +00:00
|
|
|
}
|
2014-03-28 00:54:47 +00:00
|
|
|
outpdimm->lowest_common_spd_caslat = caslat_actual;
|
|
|
|
debug("lowest_common_spd_caslat is 0x%x\n", caslat_actual);
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
#else /* for DDR1 and DDR2 */
|
|
|
|
static unsigned int
|
2015-01-06 21:18:50 +00:00
|
|
|
compute_cas_latency(const unsigned int ctrl_num,
|
|
|
|
const dimm_params_t *dimm_params,
|
2014-03-28 00:54:47 +00:00
|
|
|
common_timing_params_t *outpdimm,
|
|
|
|
unsigned int number_of_dimms)
|
|
|
|
{
|
|
|
|
int i;
|
2015-01-06 21:18:50 +00:00
|
|
|
const unsigned int mclk_ps = get_memory_clk_period_ps(ctrl_num);
|
2014-03-28 00:54:47 +00:00
|
|
|
unsigned int lowest_good_caslat;
|
|
|
|
unsigned int not_ok;
|
|
|
|
unsigned int temp1, temp2;
|
|
|
|
|
|
|
|
debug("using mclk_ps = %u\n", mclk_ps);
|
|
|
|
if (mclk_ps > outpdimm->tckmax_ps) {
|
|
|
|
printf("Warning: DDR clock (%u ps) is slower than DIMM(s) (tCKmax %u ps)\n",
|
|
|
|
mclk_ps, outpdimm->tckmax_ps);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Compute a CAS latency suitable for all DIMMs
|
|
|
|
*
|
|
|
|
* Strategy for SPD-defined latencies: compute only
|
|
|
|
* CAS latency defined by all DIMMs.
|
|
|
|
*/
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Step 1: find CAS latency common to all DIMMs using bitwise
|
|
|
|
* operation.
|
|
|
|
*/
|
|
|
|
temp1 = 0xFF;
|
|
|
|
for (i = 0; i < number_of_dimms; i++) {
|
|
|
|
if (dimm_params[i].n_ranks) {
|
|
|
|
temp2 = 0;
|
|
|
|
temp2 |= 1 << dimm_params[i].caslat_x;
|
|
|
|
temp2 |= 1 << dimm_params[i].caslat_x_minus_1;
|
|
|
|
temp2 |= 1 << dimm_params[i].caslat_x_minus_2;
|
|
|
|
/*
|
|
|
|
* If there was no entry for X-2 (X-1) in
|
|
|
|
* the SPD, then caslat_x_minus_2
|
|
|
|
* (caslat_x_minus_1) contains either 255 or
|
|
|
|
* 0xFFFFFFFF because that's what the glorious
|
|
|
|
* __ilog2 function returns for an input of 0.
|
|
|
|
* On 32-bit PowerPC, left shift counts with bit
|
|
|
|
* 26 set (that the value of 255 or 0xFFFFFFFF
|
|
|
|
* will have), cause the destination register to
|
|
|
|
* be 0. That is why this works.
|
|
|
|
*/
|
|
|
|
temp1 &= temp2;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Step 2: check each common CAS latency against tCK of each
|
|
|
|
* DIMM's SPD.
|
|
|
|
*/
|
|
|
|
lowest_good_caslat = 0;
|
|
|
|
temp2 = 0;
|
|
|
|
while (temp1) {
|
|
|
|
not_ok = 0;
|
|
|
|
temp2 = __ilog2(temp1);
|
|
|
|
debug("checking common caslat = %u\n", temp2);
|
|
|
|
|
|
|
|
/* Check if this CAS latency will work on all DIMMs at tCK. */
|
|
|
|
for (i = 0; i < number_of_dimms; i++) {
|
|
|
|
if (!dimm_params[i].n_ranks)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
if (dimm_params[i].caslat_x == temp2) {
|
|
|
|
if (mclk_ps >= dimm_params[i].tckmin_x_ps) {
|
|
|
|
debug("CL = %u ok on DIMM %u at tCK=%u ps with tCKmin_X_ps of %u\n",
|
|
|
|
temp2, i, mclk_ps,
|
|
|
|
dimm_params[i].tckmin_x_ps);
|
|
|
|
continue;
|
|
|
|
} else {
|
|
|
|
not_ok++;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (dimm_params[i].caslat_x_minus_1 == temp2) {
|
|
|
|
unsigned int tckmin_x_minus_1_ps
|
|
|
|
= dimm_params[i].tckmin_x_minus_1_ps;
|
|
|
|
if (mclk_ps >= tckmin_x_minus_1_ps) {
|
|
|
|
debug("CL = %u ok on DIMM %u at tCK=%u ps with tckmin_x_minus_1_ps of %u\n",
|
|
|
|
temp2, i, mclk_ps,
|
|
|
|
tckmin_x_minus_1_ps);
|
|
|
|
continue;
|
|
|
|
} else {
|
|
|
|
not_ok++;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (dimm_params[i].caslat_x_minus_2 == temp2) {
|
|
|
|
unsigned int tckmin_x_minus_2_ps
|
|
|
|
= dimm_params[i].tckmin_x_minus_2_ps;
|
|
|
|
if (mclk_ps >= tckmin_x_minus_2_ps) {
|
|
|
|
debug("CL = %u ok on DIMM %u at tCK=%u ps with tckmin_x_minus_2_ps of %u\n",
|
|
|
|
temp2, i, mclk_ps,
|
|
|
|
tckmin_x_minus_2_ps);
|
|
|
|
continue;
|
|
|
|
} else {
|
|
|
|
not_ok++;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!not_ok)
|
|
|
|
lowest_good_caslat = temp2;
|
|
|
|
|
|
|
|
temp1 &= ~(1 << temp2);
|
|
|
|
}
|
|
|
|
|
|
|
|
debug("lowest common SPD-defined CAS latency = %u\n",
|
|
|
|
lowest_good_caslat);
|
|
|
|
outpdimm->lowest_common_spd_caslat = lowest_good_caslat;
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Compute a common 'de-rated' CAS latency.
|
|
|
|
*
|
|
|
|
* The strategy here is to find the *highest* dereated cas latency
|
|
|
|
* with the assumption that all of the DIMMs will support a dereated
|
|
|
|
* CAS latency higher than or equal to their lowest dereated value.
|
|
|
|
*/
|
|
|
|
temp1 = 0;
|
|
|
|
for (i = 0; i < number_of_dimms; i++)
|
|
|
|
temp1 = max(temp1, dimm_params[i].caslat_lowest_derated);
|
|
|
|
|
|
|
|
outpdimm->highest_common_derated_caslat = temp1;
|
|
|
|
debug("highest common dereated CAS latency = %u\n", temp1);
|
2009-03-14 04:48:30 +00:00
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
2012-10-29 13:34:37 +00:00
|
|
|
#endif
|
2009-03-14 04:48:30 +00:00
|
|
|
|
2008-08-26 20:01:29 +00:00
|
|
|
/*
|
|
|
|
* compute_lowest_common_dimm_parameters()
|
|
|
|
*
|
|
|
|
* Determine the worst-case DIMM timing parameters from the set of DIMMs
|
|
|
|
* whose parameters have been computed into the array pointed to
|
|
|
|
* by dimm_params.
|
|
|
|
*/
|
|
|
|
unsigned int
|
2015-01-06 21:18:50 +00:00
|
|
|
compute_lowest_common_dimm_parameters(const unsigned int ctrl_num,
|
|
|
|
const dimm_params_t *dimm_params,
|
2008-08-26 20:01:29 +00:00
|
|
|
common_timing_params_t *outpdimm,
|
2012-10-08 07:44:23 +00:00
|
|
|
const unsigned int number_of_dimms)
|
2008-08-26 20:01:29 +00:00
|
|
|
{
|
2010-07-02 22:25:55 +00:00
|
|
|
unsigned int i, j;
|
2008-08-26 20:01:29 +00:00
|
|
|
|
2013-09-25 05:11:19 +00:00
|
|
|
unsigned int tckmin_x_ps = 0;
|
|
|
|
unsigned int tckmax_ps = 0xFFFFFFFF;
|
|
|
|
unsigned int trcd_ps = 0;
|
|
|
|
unsigned int trp_ps = 0;
|
|
|
|
unsigned int tras_ps = 0;
|
2014-03-28 00:54:47 +00:00
|
|
|
#if defined(CONFIG_SYS_FSL_DDR3) || defined(CONFIG_SYS_FSL_DDR4)
|
|
|
|
unsigned int taamin_ps = 0;
|
|
|
|
#endif
|
|
|
|
#ifdef CONFIG_SYS_FSL_DDR4
|
|
|
|
unsigned int twr_ps = 15000;
|
|
|
|
unsigned int trfc1_ps = 0;
|
|
|
|
unsigned int trfc2_ps = 0;
|
|
|
|
unsigned int trfc4_ps = 0;
|
|
|
|
unsigned int trrds_ps = 0;
|
|
|
|
unsigned int trrdl_ps = 0;
|
|
|
|
unsigned int tccdl_ps = 0;
|
2018-01-29 17:44:35 +00:00
|
|
|
unsigned int trfc_slr_ps = 0;
|
2014-03-28 00:54:47 +00:00
|
|
|
#else
|
2013-09-25 05:11:19 +00:00
|
|
|
unsigned int twr_ps = 0;
|
|
|
|
unsigned int twtr_ps = 0;
|
|
|
|
unsigned int trfc_ps = 0;
|
|
|
|
unsigned int trrd_ps = 0;
|
2014-03-28 00:54:47 +00:00
|
|
|
unsigned int trtp_ps = 0;
|
|
|
|
#endif
|
2013-09-25 05:11:19 +00:00
|
|
|
unsigned int trc_ps = 0;
|
2008-08-26 20:01:29 +00:00
|
|
|
unsigned int refresh_rate_ps = 0;
|
2013-10-18 09:47:20 +00:00
|
|
|
unsigned int extended_op_srt = 1;
|
2014-03-28 00:54:47 +00:00
|
|
|
#if defined(CONFIG_SYS_FSL_DDR1) || defined(CONFIG_SYS_FSL_DDR2)
|
2013-09-25 05:11:19 +00:00
|
|
|
unsigned int tis_ps = 0;
|
|
|
|
unsigned int tih_ps = 0;
|
|
|
|
unsigned int tds_ps = 0;
|
|
|
|
unsigned int tdh_ps = 0;
|
|
|
|
unsigned int tdqsq_max_ps = 0;
|
|
|
|
unsigned int tqhs_ps = 0;
|
2014-03-28 00:54:47 +00:00
|
|
|
#endif
|
2011-01-10 12:02:56 +00:00
|
|
|
unsigned int temp1, temp2;
|
2008-08-26 20:01:29 +00:00
|
|
|
unsigned int additive_latency = 0;
|
|
|
|
|
|
|
|
temp1 = 0;
|
|
|
|
for (i = 0; i < number_of_dimms; i++) {
|
|
|
|
/*
|
|
|
|
* If there are no ranks on this DIMM,
|
|
|
|
* it probably doesn't exist, so skip it.
|
|
|
|
*/
|
|
|
|
if (dimm_params[i].n_ranks == 0) {
|
|
|
|
temp1++;
|
|
|
|
continue;
|
|
|
|
}
|
2010-07-02 22:25:53 +00:00
|
|
|
if (dimm_params[i].n_ranks == 4 && i != 0) {
|
|
|
|
printf("Found Quad-rank DIMM in wrong bank, ignored."
|
|
|
|
" Software may not run as expected.\n");
|
|
|
|
temp1++;
|
|
|
|
continue;
|
|
|
|
}
|
2012-10-08 07:44:23 +00:00
|
|
|
|
|
|
|
/*
|
|
|
|
* check if quad-rank DIMM is plugged if
|
|
|
|
* CONFIG_CHIP_SELECT_QUAD_CAPABLE is not defined
|
|
|
|
* Only the board with proper design is capable
|
|
|
|
*/
|
|
|
|
#ifndef CONFIG_FSL_DDR_FIRST_SLOT_QUAD_CAPABLE
|
2010-07-02 22:25:53 +00:00
|
|
|
if (dimm_params[i].n_ranks == 4 && \
|
|
|
|
CONFIG_CHIP_SELECTS_PER_CTRL/CONFIG_DIMM_SLOTS_PER_CTLR < 4) {
|
|
|
|
printf("Found Quad-rank DIMM, not able to support.");
|
|
|
|
temp1++;
|
|
|
|
continue;
|
|
|
|
}
|
2012-10-08 07:44:23 +00:00
|
|
|
#endif
|
2008-08-26 20:01:29 +00:00
|
|
|
/*
|
2013-09-25 05:11:19 +00:00
|
|
|
* Find minimum tckmax_ps to find fastest slow speed,
|
2008-08-26 20:01:29 +00:00
|
|
|
* i.e., this is the slowest the whole system can go.
|
|
|
|
*/
|
linux/kernel.h: sync min, max, min3, max3 macros with Linux
U-Boot has never cared about the type when we get max/min of two
values, but Linux Kernel does. This commit gets min, max, min3, max3
macros synced with the kernel introducing type checks.
Many of references of those macros must be fixed to suppress warnings.
We have two options:
- Use min, max, min3, max3 only when the arguments have the same type
(or add casts to the arguments)
- Use min_t/max_t instead with the appropriate type for the first
argument
Signed-off-by: Masahiro Yamada <yamada.m@jp.panasonic.com>
Acked-by: Pavel Machek <pavel@denx.de>
Acked-by: Lukasz Majewski <l.majewski@samsung.com>
Tested-by: Lukasz Majewski <l.majewski@samsung.com>
[trini: Fixup arch/blackfin/lib/string.c]
Signed-off-by: Tom Rini <trini@ti.com>
2014-11-06 18:03:31 +00:00
|
|
|
tckmax_ps = min(tckmax_ps,
|
|
|
|
(unsigned int)dimm_params[i].tckmax_ps);
|
2014-03-28 00:54:47 +00:00
|
|
|
#if defined(CONFIG_SYS_FSL_DDR3) || defined(CONFIG_SYS_FSL_DDR4)
|
linux/kernel.h: sync min, max, min3, max3 macros with Linux
U-Boot has never cared about the type when we get max/min of two
values, but Linux Kernel does. This commit gets min, max, min3, max3
macros synced with the kernel introducing type checks.
Many of references of those macros must be fixed to suppress warnings.
We have two options:
- Use min, max, min3, max3 only when the arguments have the same type
(or add casts to the arguments)
- Use min_t/max_t instead with the appropriate type for the first
argument
Signed-off-by: Masahiro Yamada <yamada.m@jp.panasonic.com>
Acked-by: Pavel Machek <pavel@denx.de>
Acked-by: Lukasz Majewski <l.majewski@samsung.com>
Tested-by: Lukasz Majewski <l.majewski@samsung.com>
[trini: Fixup arch/blackfin/lib/string.c]
Signed-off-by: Tom Rini <trini@ti.com>
2014-11-06 18:03:31 +00:00
|
|
|
taamin_ps = max(taamin_ps,
|
|
|
|
(unsigned int)dimm_params[i].taa_ps);
|
2014-03-28 00:54:47 +00:00
|
|
|
#endif
|
linux/kernel.h: sync min, max, min3, max3 macros with Linux
U-Boot has never cared about the type when we get max/min of two
values, but Linux Kernel does. This commit gets min, max, min3, max3
macros synced with the kernel introducing type checks.
Many of references of those macros must be fixed to suppress warnings.
We have two options:
- Use min, max, min3, max3 only when the arguments have the same type
(or add casts to the arguments)
- Use min_t/max_t instead with the appropriate type for the first
argument
Signed-off-by: Masahiro Yamada <yamada.m@jp.panasonic.com>
Acked-by: Pavel Machek <pavel@denx.de>
Acked-by: Lukasz Majewski <l.majewski@samsung.com>
Tested-by: Lukasz Majewski <l.majewski@samsung.com>
[trini: Fixup arch/blackfin/lib/string.c]
Signed-off-by: Tom Rini <trini@ti.com>
2014-11-06 18:03:31 +00:00
|
|
|
tckmin_x_ps = max(tckmin_x_ps,
|
|
|
|
(unsigned int)dimm_params[i].tckmin_x_ps);
|
|
|
|
trcd_ps = max(trcd_ps, (unsigned int)dimm_params[i].trcd_ps);
|
|
|
|
trp_ps = max(trp_ps, (unsigned int)dimm_params[i].trp_ps);
|
|
|
|
tras_ps = max(tras_ps, (unsigned int)dimm_params[i].tras_ps);
|
2014-03-28 00:54:47 +00:00
|
|
|
#ifdef CONFIG_SYS_FSL_DDR4
|
linux/kernel.h: sync min, max, min3, max3 macros with Linux
U-Boot has never cared about the type when we get max/min of two
values, but Linux Kernel does. This commit gets min, max, min3, max3
macros synced with the kernel introducing type checks.
Many of references of those macros must be fixed to suppress warnings.
We have two options:
- Use min, max, min3, max3 only when the arguments have the same type
(or add casts to the arguments)
- Use min_t/max_t instead with the appropriate type for the first
argument
Signed-off-by: Masahiro Yamada <yamada.m@jp.panasonic.com>
Acked-by: Pavel Machek <pavel@denx.de>
Acked-by: Lukasz Majewski <l.majewski@samsung.com>
Tested-by: Lukasz Majewski <l.majewski@samsung.com>
[trini: Fixup arch/blackfin/lib/string.c]
Signed-off-by: Tom Rini <trini@ti.com>
2014-11-06 18:03:31 +00:00
|
|
|
trfc1_ps = max(trfc1_ps,
|
|
|
|
(unsigned int)dimm_params[i].trfc1_ps);
|
|
|
|
trfc2_ps = max(trfc2_ps,
|
|
|
|
(unsigned int)dimm_params[i].trfc2_ps);
|
|
|
|
trfc4_ps = max(trfc4_ps,
|
|
|
|
(unsigned int)dimm_params[i].trfc4_ps);
|
|
|
|
trrds_ps = max(trrds_ps,
|
|
|
|
(unsigned int)dimm_params[i].trrds_ps);
|
|
|
|
trrdl_ps = max(trrdl_ps,
|
|
|
|
(unsigned int)dimm_params[i].trrdl_ps);
|
|
|
|
tccdl_ps = max(tccdl_ps,
|
|
|
|
(unsigned int)dimm_params[i].tccdl_ps);
|
2018-01-29 17:44:35 +00:00
|
|
|
trfc_slr_ps = max(trfc_slr_ps,
|
|
|
|
(unsigned int)dimm_params[i].trfc_slr_ps);
|
2014-03-28 00:54:47 +00:00
|
|
|
#else
|
linux/kernel.h: sync min, max, min3, max3 macros with Linux
U-Boot has never cared about the type when we get max/min of two
values, but Linux Kernel does. This commit gets min, max, min3, max3
macros synced with the kernel introducing type checks.
Many of references of those macros must be fixed to suppress warnings.
We have two options:
- Use min, max, min3, max3 only when the arguments have the same type
(or add casts to the arguments)
- Use min_t/max_t instead with the appropriate type for the first
argument
Signed-off-by: Masahiro Yamada <yamada.m@jp.panasonic.com>
Acked-by: Pavel Machek <pavel@denx.de>
Acked-by: Lukasz Majewski <l.majewski@samsung.com>
Tested-by: Lukasz Majewski <l.majewski@samsung.com>
[trini: Fixup arch/blackfin/lib/string.c]
Signed-off-by: Tom Rini <trini@ti.com>
2014-11-06 18:03:31 +00:00
|
|
|
twr_ps = max(twr_ps, (unsigned int)dimm_params[i].twr_ps);
|
|
|
|
twtr_ps = max(twtr_ps, (unsigned int)dimm_params[i].twtr_ps);
|
|
|
|
trfc_ps = max(trfc_ps, (unsigned int)dimm_params[i].trfc_ps);
|
|
|
|
trrd_ps = max(trrd_ps, (unsigned int)dimm_params[i].trrd_ps);
|
|
|
|
trtp_ps = max(trtp_ps, (unsigned int)dimm_params[i].trtp_ps);
|
2014-03-28 00:54:47 +00:00
|
|
|
#endif
|
linux/kernel.h: sync min, max, min3, max3 macros with Linux
U-Boot has never cared about the type when we get max/min of two
values, but Linux Kernel does. This commit gets min, max, min3, max3
macros synced with the kernel introducing type checks.
Many of references of those macros must be fixed to suppress warnings.
We have two options:
- Use min, max, min3, max3 only when the arguments have the same type
(or add casts to the arguments)
- Use min_t/max_t instead with the appropriate type for the first
argument
Signed-off-by: Masahiro Yamada <yamada.m@jp.panasonic.com>
Acked-by: Pavel Machek <pavel@denx.de>
Acked-by: Lukasz Majewski <l.majewski@samsung.com>
Tested-by: Lukasz Majewski <l.majewski@samsung.com>
[trini: Fixup arch/blackfin/lib/string.c]
Signed-off-by: Tom Rini <trini@ti.com>
2014-11-06 18:03:31 +00:00
|
|
|
trc_ps = max(trc_ps, (unsigned int)dimm_params[i].trc_ps);
|
2014-03-28 00:54:47 +00:00
|
|
|
#if defined(CONFIG_SYS_FSL_DDR1) || defined(CONFIG_SYS_FSL_DDR2)
|
linux/kernel.h: sync min, max, min3, max3 macros with Linux
U-Boot has never cared about the type when we get max/min of two
values, but Linux Kernel does. This commit gets min, max, min3, max3
macros synced with the kernel introducing type checks.
Many of references of those macros must be fixed to suppress warnings.
We have two options:
- Use min, max, min3, max3 only when the arguments have the same type
(or add casts to the arguments)
- Use min_t/max_t instead with the appropriate type for the first
argument
Signed-off-by: Masahiro Yamada <yamada.m@jp.panasonic.com>
Acked-by: Pavel Machek <pavel@denx.de>
Acked-by: Lukasz Majewski <l.majewski@samsung.com>
Tested-by: Lukasz Majewski <l.majewski@samsung.com>
[trini: Fixup arch/blackfin/lib/string.c]
Signed-off-by: Tom Rini <trini@ti.com>
2014-11-06 18:03:31 +00:00
|
|
|
tis_ps = max(tis_ps, (unsigned int)dimm_params[i].tis_ps);
|
|
|
|
tih_ps = max(tih_ps, (unsigned int)dimm_params[i].tih_ps);
|
|
|
|
tds_ps = max(tds_ps, (unsigned int)dimm_params[i].tds_ps);
|
|
|
|
tdh_ps = max(tdh_ps, (unsigned int)dimm_params[i].tdh_ps);
|
|
|
|
tqhs_ps = max(tqhs_ps, (unsigned int)dimm_params[i].tqhs_ps);
|
2008-08-26 20:01:29 +00:00
|
|
|
/*
|
2013-09-25 05:11:19 +00:00
|
|
|
* Find maximum tdqsq_max_ps to find slowest.
|
2008-08-26 20:01:29 +00:00
|
|
|
*
|
|
|
|
* FIXME: is finding the slowest value the correct
|
|
|
|
* strategy for this parameter?
|
|
|
|
*/
|
linux/kernel.h: sync min, max, min3, max3 macros with Linux
U-Boot has never cared about the type when we get max/min of two
values, but Linux Kernel does. This commit gets min, max, min3, max3
macros synced with the kernel introducing type checks.
Many of references of those macros must be fixed to suppress warnings.
We have two options:
- Use min, max, min3, max3 only when the arguments have the same type
(or add casts to the arguments)
- Use min_t/max_t instead with the appropriate type for the first
argument
Signed-off-by: Masahiro Yamada <yamada.m@jp.panasonic.com>
Acked-by: Pavel Machek <pavel@denx.de>
Acked-by: Lukasz Majewski <l.majewski@samsung.com>
Tested-by: Lukasz Majewski <l.majewski@samsung.com>
[trini: Fixup arch/blackfin/lib/string.c]
Signed-off-by: Tom Rini <trini@ti.com>
2014-11-06 18:03:31 +00:00
|
|
|
tdqsq_max_ps = max(tdqsq_max_ps,
|
|
|
|
(unsigned int)dimm_params[i].tdqsq_max_ps);
|
2014-03-28 00:54:47 +00:00
|
|
|
#endif
|
|
|
|
refresh_rate_ps = max(refresh_rate_ps,
|
linux/kernel.h: sync min, max, min3, max3 macros with Linux
U-Boot has never cared about the type when we get max/min of two
values, but Linux Kernel does. This commit gets min, max, min3, max3
macros synced with the kernel introducing type checks.
Many of references of those macros must be fixed to suppress warnings.
We have two options:
- Use min, max, min3, max3 only when the arguments have the same type
(or add casts to the arguments)
- Use min_t/max_t instead with the appropriate type for the first
argument
Signed-off-by: Masahiro Yamada <yamada.m@jp.panasonic.com>
Acked-by: Pavel Machek <pavel@denx.de>
Acked-by: Lukasz Majewski <l.majewski@samsung.com>
Tested-by: Lukasz Majewski <l.majewski@samsung.com>
[trini: Fixup arch/blackfin/lib/string.c]
Signed-off-by: Tom Rini <trini@ti.com>
2014-11-06 18:03:31 +00:00
|
|
|
(unsigned int)dimm_params[i].refresh_rate_ps);
|
2014-03-28 00:54:47 +00:00
|
|
|
/* extended_op_srt is either 0 or 1, 0 having priority */
|
|
|
|
extended_op_srt = min(extended_op_srt,
|
linux/kernel.h: sync min, max, min3, max3 macros with Linux
U-Boot has never cared about the type when we get max/min of two
values, but Linux Kernel does. This commit gets min, max, min3, max3
macros synced with the kernel introducing type checks.
Many of references of those macros must be fixed to suppress warnings.
We have two options:
- Use min, max, min3, max3 only when the arguments have the same type
(or add casts to the arguments)
- Use min_t/max_t instead with the appropriate type for the first
argument
Signed-off-by: Masahiro Yamada <yamada.m@jp.panasonic.com>
Acked-by: Pavel Machek <pavel@denx.de>
Acked-by: Lukasz Majewski <l.majewski@samsung.com>
Tested-by: Lukasz Majewski <l.majewski@samsung.com>
[trini: Fixup arch/blackfin/lib/string.c]
Signed-off-by: Tom Rini <trini@ti.com>
2014-11-06 18:03:31 +00:00
|
|
|
(unsigned int)dimm_params[i].extended_op_srt);
|
2008-08-26 20:01:29 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
outpdimm->ndimms_present = number_of_dimms - temp1;
|
|
|
|
|
|
|
|
if (temp1 == number_of_dimms) {
|
|
|
|
debug("no dimms this memory controller\n");
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2013-09-25 05:11:19 +00:00
|
|
|
outpdimm->tckmin_x_ps = tckmin_x_ps;
|
|
|
|
outpdimm->tckmax_ps = tckmax_ps;
|
2014-03-28 00:54:47 +00:00
|
|
|
#if defined(CONFIG_SYS_FSL_DDR3) || defined(CONFIG_SYS_FSL_DDR4)
|
|
|
|
outpdimm->taamin_ps = taamin_ps;
|
|
|
|
#endif
|
2013-09-25 05:11:19 +00:00
|
|
|
outpdimm->trcd_ps = trcd_ps;
|
|
|
|
outpdimm->trp_ps = trp_ps;
|
|
|
|
outpdimm->tras_ps = tras_ps;
|
2014-03-28 00:54:47 +00:00
|
|
|
#ifdef CONFIG_SYS_FSL_DDR4
|
|
|
|
outpdimm->trfc1_ps = trfc1_ps;
|
|
|
|
outpdimm->trfc2_ps = trfc2_ps;
|
|
|
|
outpdimm->trfc4_ps = trfc4_ps;
|
|
|
|
outpdimm->trrds_ps = trrds_ps;
|
|
|
|
outpdimm->trrdl_ps = trrdl_ps;
|
|
|
|
outpdimm->tccdl_ps = tccdl_ps;
|
2018-01-29 17:44:35 +00:00
|
|
|
outpdimm->trfc_slr_ps = trfc_slr_ps;
|
2014-03-28 00:54:47 +00:00
|
|
|
#else
|
2013-09-25 05:11:19 +00:00
|
|
|
outpdimm->twtr_ps = twtr_ps;
|
|
|
|
outpdimm->trfc_ps = trfc_ps;
|
|
|
|
outpdimm->trrd_ps = trrd_ps;
|
2014-03-28 00:54:47 +00:00
|
|
|
outpdimm->trtp_ps = trtp_ps;
|
|
|
|
#endif
|
|
|
|
outpdimm->twr_ps = twr_ps;
|
2013-09-25 05:11:19 +00:00
|
|
|
outpdimm->trc_ps = trc_ps;
|
2008-08-26 20:01:29 +00:00
|
|
|
outpdimm->refresh_rate_ps = refresh_rate_ps;
|
2013-10-18 09:47:20 +00:00
|
|
|
outpdimm->extended_op_srt = extended_op_srt;
|
2014-03-28 00:54:47 +00:00
|
|
|
#if defined(CONFIG_SYS_FSL_DDR1) || defined(CONFIG_SYS_FSL_DDR2)
|
2013-09-25 05:11:19 +00:00
|
|
|
outpdimm->tis_ps = tis_ps;
|
|
|
|
outpdimm->tih_ps = tih_ps;
|
|
|
|
outpdimm->tds_ps = tds_ps;
|
|
|
|
outpdimm->tdh_ps = tdh_ps;
|
|
|
|
outpdimm->tdqsq_max_ps = tdqsq_max_ps;
|
|
|
|
outpdimm->tqhs_ps = tqhs_ps;
|
2014-03-28 00:54:47 +00:00
|
|
|
#endif
|
2008-08-26 20:01:29 +00:00
|
|
|
|
|
|
|
/* Determine common burst length for all DIMMs. */
|
|
|
|
temp1 = 0xff;
|
|
|
|
for (i = 0; i < number_of_dimms; i++) {
|
|
|
|
if (dimm_params[i].n_ranks) {
|
|
|
|
temp1 &= dimm_params[i].burst_lengths_bitmask;
|
|
|
|
}
|
|
|
|
}
|
2013-09-25 05:11:19 +00:00
|
|
|
outpdimm->all_dimms_burst_lengths_bitmask = temp1;
|
2008-08-26 20:01:29 +00:00
|
|
|
|
|
|
|
/* Determine if all DIMMs registered buffered. */
|
|
|
|
temp1 = temp2 = 0;
|
|
|
|
for (i = 0; i < number_of_dimms; i++) {
|
|
|
|
if (dimm_params[i].n_ranks) {
|
2011-02-04 21:58:00 +00:00
|
|
|
if (dimm_params[i].registered_dimm) {
|
2008-08-26 20:01:29 +00:00
|
|
|
temp1 = 1;
|
2013-08-16 07:16:11 +00:00
|
|
|
#ifndef CONFIG_SPL_BUILD
|
2011-02-04 21:58:00 +00:00
|
|
|
printf("Detected RDIMM %s\n",
|
|
|
|
dimm_params[i].mpart);
|
2013-08-16 07:16:11 +00:00
|
|
|
#endif
|
2011-02-04 21:58:00 +00:00
|
|
|
} else {
|
2008-08-26 20:01:29 +00:00
|
|
|
temp2 = 1;
|
2013-08-16 07:16:11 +00:00
|
|
|
#ifndef CONFIG_SPL_BUILD
|
2011-02-04 21:58:00 +00:00
|
|
|
printf("Detected UDIMM %s\n",
|
|
|
|
dimm_params[i].mpart);
|
2013-08-16 07:16:11 +00:00
|
|
|
#endif
|
2011-02-04 21:58:00 +00:00
|
|
|
}
|
2022-09-09 15:32:45 +00:00
|
|
|
#ifndef CONFIG_SPL_BUILD
|
|
|
|
puts(" ");
|
|
|
|
#endif
|
2008-08-26 20:01:29 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-09-25 05:11:19 +00:00
|
|
|
outpdimm->all_dimms_registered = 0;
|
|
|
|
outpdimm->all_dimms_unbuffered = 0;
|
2008-08-26 20:01:29 +00:00
|
|
|
if (temp1 && !temp2) {
|
2013-09-25 05:11:19 +00:00
|
|
|
outpdimm->all_dimms_registered = 1;
|
2011-01-10 12:02:56 +00:00
|
|
|
} else if (!temp1 && temp2) {
|
2013-09-25 05:11:19 +00:00
|
|
|
outpdimm->all_dimms_unbuffered = 1;
|
2011-01-10 12:02:56 +00:00
|
|
|
} else {
|
2008-08-26 20:01:29 +00:00
|
|
|
printf("ERROR: Mix of registered buffered and unbuffered "
|
|
|
|
"DIMMs detected!\n");
|
|
|
|
}
|
|
|
|
|
2010-07-02 22:25:55 +00:00
|
|
|
temp1 = 0;
|
2013-09-25 05:11:19 +00:00
|
|
|
if (outpdimm->all_dimms_registered)
|
2010-07-02 22:25:55 +00:00
|
|
|
for (j = 0; j < 16; j++) {
|
|
|
|
outpdimm->rcw[j] = dimm_params[0].rcw[j];
|
2012-10-08 07:44:23 +00:00
|
|
|
for (i = 1; i < number_of_dimms; i++) {
|
|
|
|
if (!dimm_params[i].n_ranks)
|
|
|
|
continue;
|
2010-07-02 22:25:55 +00:00
|
|
|
if (dimm_params[i].rcw[j] != dimm_params[0].rcw[j]) {
|
2011-01-10 12:02:56 +00:00
|
|
|
temp1 = 1;
|
2010-07-02 22:25:55 +00:00
|
|
|
break;
|
|
|
|
}
|
2012-10-08 07:44:23 +00:00
|
|
|
}
|
2010-07-02 22:25:55 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if (temp1 != 0)
|
|
|
|
printf("ERROR: Mix different RDIMM detected!\n");
|
|
|
|
|
2014-03-28 00:54:47 +00:00
|
|
|
/* calculate cas latency for all DDR types */
|
2015-01-06 21:18:50 +00:00
|
|
|
if (compute_cas_latency(ctrl_num, dimm_params,
|
|
|
|
outpdimm, number_of_dimms))
|
2009-03-14 04:48:30 +00:00
|
|
|
return 1;
|
2008-08-26 20:01:29 +00:00
|
|
|
|
|
|
|
/* Determine if all DIMMs ECC capable. */
|
|
|
|
temp1 = 1;
|
|
|
|
for (i = 0; i < number_of_dimms; i++) {
|
2011-03-17 18:18:10 +00:00
|
|
|
if (dimm_params[i].n_ranks &&
|
|
|
|
!(dimm_params[i].edc_config & EDC_ECC)) {
|
2008-08-26 20:01:29 +00:00
|
|
|
temp1 = 0;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (temp1) {
|
|
|
|
debug("all DIMMs ECC capable\n");
|
|
|
|
} else {
|
|
|
|
debug("Warning: not all DIMMs ECC capable, cant enable ECC\n");
|
|
|
|
}
|
2013-09-25 05:11:19 +00:00
|
|
|
outpdimm->all_dimms_ecc_capable = temp1;
|
2008-08-26 20:01:29 +00:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Compute additive latency.
|
|
|
|
*
|
|
|
|
* For DDR1, additive latency should be 0.
|
|
|
|
*
|
|
|
|
* For DDR2, with ODT enabled, use "a value" less than ACTTORW,
|
|
|
|
* which comes from Trcd, and also note that:
|
|
|
|
* add_lat + caslat must be >= 4
|
|
|
|
*
|
2009-03-14 04:48:30 +00:00
|
|
|
* For DDR3, we use the AL=0
|
2008-08-26 20:01:29 +00:00
|
|
|
*
|
|
|
|
* When to use additive latency for DDR2:
|
|
|
|
*
|
|
|
|
* I. Because you are using CL=3 and need to do ODT on writes and
|
|
|
|
* want functionality.
|
|
|
|
* 1. Are you going to use ODT? (Does your board not have
|
|
|
|
* additional termination circuitry for DQ, DQS, DQS_,
|
|
|
|
* DM, RDQS, RDQS_ for x4/x8 configs?)
|
|
|
|
* 2. If so, is your lowest supported CL going to be 3?
|
|
|
|
* 3. If so, then you must set AL=1 because
|
|
|
|
*
|
|
|
|
* WL >= 3 for ODT on writes
|
|
|
|
* RL = AL + CL
|
|
|
|
* WL = RL - 1
|
|
|
|
* ->
|
|
|
|
* WL = AL + CL - 1
|
|
|
|
* AL + CL - 1 >= 3
|
|
|
|
* AL + CL >= 4
|
|
|
|
* QED
|
|
|
|
*
|
|
|
|
* RL >= 3 for ODT on reads
|
|
|
|
* RL = AL + CL
|
|
|
|
*
|
|
|
|
* Since CL aren't usually less than 2, AL=0 is a minimum,
|
|
|
|
* so the WL-derived AL should be the -- FIXME?
|
|
|
|
*
|
|
|
|
* II. Because you are using auto-precharge globally and want to
|
|
|
|
* use additive latency (posted CAS) to get more bandwidth.
|
|
|
|
* 1. Are you going to use auto-precharge mode globally?
|
|
|
|
*
|
|
|
|
* Use addtivie latency and compute AL to be 1 cycle less than
|
|
|
|
* tRCD, i.e. the READ or WRITE command is in the cycle
|
|
|
|
* immediately following the ACTIVATE command..
|
|
|
|
*
|
|
|
|
* III. Because you feel like it or want to do some sort of
|
|
|
|
* degraded-performance experiment.
|
|
|
|
* 1. Do you just want to use additive latency because you feel
|
|
|
|
* like it?
|
|
|
|
*
|
|
|
|
* Validation: AL is less than tRCD, and within the other
|
|
|
|
* read-to-precharge constraints.
|
|
|
|
*/
|
|
|
|
|
|
|
|
additive_latency = 0;
|
|
|
|
|
2013-09-30 16:22:09 +00:00
|
|
|
#if defined(CONFIG_SYS_FSL_DDR2)
|
2014-03-28 00:54:47 +00:00
|
|
|
if ((outpdimm->lowest_common_spd_caslat < 4) &&
|
2015-01-06 21:18:50 +00:00
|
|
|
(picos_to_mclk(ctrl_num, trcd_ps) >
|
|
|
|
outpdimm->lowest_common_spd_caslat)) {
|
|
|
|
additive_latency = picos_to_mclk(ctrl_num, trcd_ps) -
|
2014-03-28 00:54:47 +00:00
|
|
|
outpdimm->lowest_common_spd_caslat;
|
2015-01-06 21:18:50 +00:00
|
|
|
if (mclk_to_picos(ctrl_num, additive_latency) > trcd_ps) {
|
|
|
|
additive_latency = picos_to_mclk(ctrl_num, trcd_ps);
|
2008-08-26 20:01:29 +00:00
|
|
|
debug("setting additive_latency to %u because it was "
|
|
|
|
" greater than tRCD_ps\n", additive_latency);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Validate additive latency
|
|
|
|
*
|
|
|
|
* AL <= tRCD(min)
|
|
|
|
*/
|
2015-01-06 21:18:50 +00:00
|
|
|
if (mclk_to_picos(ctrl_num, additive_latency) > trcd_ps) {
|
2008-08-26 20:01:29 +00:00
|
|
|
printf("Error: invalid additive latency exceeds tRCD(min).\n");
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* RL = CL + AL; RL >= 3 for ODT_RD_CFG to be enabled
|
|
|
|
* WL = RL - 1; WL >= 3 for ODT_WL_CFG to be enabled
|
|
|
|
* ADD_LAT (the register) must be set to a value less
|
|
|
|
* than ACTTORW if WL = 1, then AL must be set to 1
|
|
|
|
* RD_TO_PRE (the register) must be set to a minimum
|
|
|
|
* tRTP + AL if AL is nonzero
|
|
|
|
*/
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Additive latency will be applied only if the memctl option to
|
|
|
|
* use it.
|
|
|
|
*/
|
|
|
|
outpdimm->additive_latency = additive_latency;
|
|
|
|
|
2013-09-25 05:11:19 +00:00
|
|
|
debug("tCKmin_ps = %u\n", outpdimm->tckmin_x_ps);
|
|
|
|
debug("trcd_ps = %u\n", outpdimm->trcd_ps);
|
|
|
|
debug("trp_ps = %u\n", outpdimm->trp_ps);
|
|
|
|
debug("tras_ps = %u\n", outpdimm->tras_ps);
|
2014-03-28 00:54:47 +00:00
|
|
|
#ifdef CONFIG_SYS_FSL_DDR4
|
|
|
|
debug("trfc1_ps = %u\n", trfc1_ps);
|
|
|
|
debug("trfc2_ps = %u\n", trfc2_ps);
|
|
|
|
debug("trfc4_ps = %u\n", trfc4_ps);
|
|
|
|
debug("trrds_ps = %u\n", trrds_ps);
|
|
|
|
debug("trrdl_ps = %u\n", trrdl_ps);
|
|
|
|
debug("tccdl_ps = %u\n", tccdl_ps);
|
2018-01-29 17:44:35 +00:00
|
|
|
debug("trfc_slr_ps = %u\n", trfc_slr_ps);
|
2014-03-28 00:54:47 +00:00
|
|
|
#else
|
2013-09-25 05:11:19 +00:00
|
|
|
debug("twtr_ps = %u\n", outpdimm->twtr_ps);
|
|
|
|
debug("trfc_ps = %u\n", outpdimm->trfc_ps);
|
|
|
|
debug("trrd_ps = %u\n", outpdimm->trrd_ps);
|
2014-03-28 00:54:47 +00:00
|
|
|
#endif
|
|
|
|
debug("twr_ps = %u\n", outpdimm->twr_ps);
|
2013-09-25 05:11:19 +00:00
|
|
|
debug("trc_ps = %u\n", outpdimm->trc_ps);
|
2012-08-17 08:22:40 +00:00
|
|
|
|
2008-08-26 20:01:29 +00:00
|
|
|
return 0;
|
|
|
|
}
|