intel/gm45: Handle overflows during DDR3 read training
We halted the machine on any overflow during the read training. However, overflows during the search for a good to bad edge are non-fatal, and should be ignored. Change-Id: I77085840ade25bce955480689c84603334113d1f Signed-off-by: Nico Huber <nico.huber@secunet.com> Reviewed-on: http://review.coreboot.org/3254 Tested-by: build bot (Jenkins) Reviewed-by: Stefan Reinauer <stefan.reinauer@coreboot.org>
This commit is contained in:
parent
26a6435123
commit
35e45c0780
@ -56,7 +56,7 @@ typedef struct {
|
|||||||
int t;
|
int t;
|
||||||
int p;
|
int p;
|
||||||
} read_timing_t;
|
} read_timing_t;
|
||||||
static void normalize_read_timing(read_timing_t *const timing)
|
static int normalize_read_timing(read_timing_t *const timing)
|
||||||
{
|
{
|
||||||
while (timing->p >= READ_TIMING_P_BOUND) {
|
while (timing->p >= READ_TIMING_P_BOUND) {
|
||||||
timing->t++;
|
timing->t++;
|
||||||
@ -66,18 +66,33 @@ static void normalize_read_timing(read_timing_t *const timing)
|
|||||||
timing->t--;
|
timing->t--;
|
||||||
timing->p += READ_TIMING_P_BOUND;
|
timing->p += READ_TIMING_P_BOUND;
|
||||||
}
|
}
|
||||||
if ((timing->t < 0) || (timing->t >= READ_TIMING_T_BOUND))
|
if (timing->t < 0) {
|
||||||
die("Timing under-/overflow during read training.\n");
|
printk(BIOS_WARNING,
|
||||||
|
"Timing underflow during read training.\n");
|
||||||
|
timing->t = 0;
|
||||||
|
timing->p = 0;
|
||||||
|
return -1;
|
||||||
|
} else if (timing->t >= READ_TIMING_T_BOUND) {
|
||||||
|
printk(BIOS_WARNING,
|
||||||
|
"Timing overflow during read training.\n");
|
||||||
|
timing->t = READ_TIMING_T_BOUND - 1;
|
||||||
|
timing->p = READ_TIMING_P_BOUND - 1;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
static void program_read_timing(const int ch, const int lane,
|
static int program_read_timing(const int ch, const int lane,
|
||||||
read_timing_t *const timing)
|
read_timing_t *const timing)
|
||||||
{
|
{
|
||||||
normalize_read_timing(timing);
|
if (normalize_read_timing(timing) < 0)
|
||||||
|
return -1;
|
||||||
|
|
||||||
u32 reg = MCHBAR32(CxRDTy_MCHBAR(ch, lane));
|
u32 reg = MCHBAR32(CxRDTy_MCHBAR(ch, lane));
|
||||||
reg &= ~(CxRDTy_T_MASK | CxRDTy_P_MASK);
|
reg &= ~(CxRDTy_T_MASK | CxRDTy_P_MASK);
|
||||||
reg |= CxRDTy_T(timing->t) | CxRDTy_P(timing->p);
|
reg |= CxRDTy_T(timing->t) | CxRDTy_P(timing->p);
|
||||||
MCHBAR32(CxRDTy_MCHBAR(ch, lane)) = reg;
|
MCHBAR32(CxRDTy_MCHBAR(ch, lane)) = reg;
|
||||||
|
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
/* Returns 1 on success, 0 on failure. */
|
/* Returns 1 on success, 0 on failure. */
|
||||||
static int read_training_test(const int channel, const int lane,
|
static int read_training_test(const int channel, const int lane,
|
||||||
@ -99,46 +114,60 @@ static int read_training_test(const int channel, const int lane,
|
|||||||
}
|
}
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
static void read_training_find_lower(const int channel, const int lane,
|
static int read_training_find_lower(const int channel, const int lane,
|
||||||
const address_bunch_t *const addresses,
|
const address_bunch_t *const addresses,
|
||||||
read_timing_t *const lower)
|
read_timing_t *const lower)
|
||||||
{
|
{
|
||||||
/* Coarse search for good t. */
|
/* Coarse search for good t. */
|
||||||
program_read_timing(channel, lane, lower);
|
program_read_timing(channel, lane, lower);
|
||||||
while (!read_training_test(channel, lane, addresses)) {
|
while (!read_training_test(channel, lane, addresses)) {
|
||||||
++lower->t;
|
++lower->t;
|
||||||
program_read_timing(channel, lane, lower);
|
if (program_read_timing(channel, lane, lower) < 0)
|
||||||
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Step back, then fine search for good p. */
|
/* Step back, then fine search for good p. */
|
||||||
if (lower->t > 0) {
|
if (lower->t <= 0)
|
||||||
--lower->t;
|
/* Can't step back, zero is good. */
|
||||||
program_read_timing(channel, lane, lower);
|
return 0;
|
||||||
while (!read_training_test(channel, lane, addresses)) {
|
|
||||||
++lower->p;
|
--lower->t;
|
||||||
program_read_timing(channel, lane, lower);
|
program_read_timing(channel, lane, lower);
|
||||||
}
|
while (!read_training_test(channel, lane, addresses)) {
|
||||||
|
++lower->p;
|
||||||
|
if (program_read_timing(channel, lane, lower) < 0)
|
||||||
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
static void read_training_find_upper(const int channel, const int lane,
|
static int read_training_find_upper(const int channel, const int lane,
|
||||||
const address_bunch_t *const addresses,
|
const address_bunch_t *const addresses,
|
||||||
read_timing_t *const upper)
|
read_timing_t *const upper)
|
||||||
{
|
{
|
||||||
program_read_timing(channel, lane, upper);
|
if (program_read_timing(channel, lane, upper) < 0)
|
||||||
if (!read_training_test(channel, lane, addresses))
|
return -1;
|
||||||
die("Read training failed: limits too narrow.\n");
|
if (!read_training_test(channel, lane, addresses)) {
|
||||||
|
printk(BIOS_WARNING,
|
||||||
|
"Read training failure: limits too narrow.\n");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
/* Coarse search for bad t. */
|
/* Coarse search for bad t. */
|
||||||
do {
|
do {
|
||||||
++upper->t;
|
++upper->t;
|
||||||
program_read_timing(channel, lane, upper);
|
if (program_read_timing(channel, lane, upper) < 0)
|
||||||
|
return -1;
|
||||||
} while (read_training_test(channel, lane, addresses));
|
} while (read_training_test(channel, lane, addresses));
|
||||||
/* Fine search for bad p. */
|
/* Fine search for bad p. */
|
||||||
--upper->t;
|
--upper->t;
|
||||||
program_read_timing(channel, lane, upper);
|
program_read_timing(channel, lane, upper);
|
||||||
while (read_training_test(channel, lane, addresses)) {
|
while (read_training_test(channel, lane, addresses)) {
|
||||||
++upper->p;
|
++upper->p;
|
||||||
program_read_timing(channel, lane, upper);
|
if (program_read_timing(channel, lane, upper) < 0)
|
||||||
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
static void read_training_per_lane(const int channel, const int lane,
|
static void read_training_per_lane(const int channel, const int lane,
|
||||||
const address_bunch_t *const addresses)
|
const address_bunch_t *const addresses)
|
||||||
@ -152,17 +181,20 @@ static void read_training_per_lane(const int channel, const int lane,
|
|||||||
/* Start at zero. */
|
/* Start at zero. */
|
||||||
lower.t = 0;
|
lower.t = 0;
|
||||||
lower.p = 0;
|
lower.p = 0;
|
||||||
read_training_find_lower(channel, lane, addresses, &lower);
|
if (read_training_find_lower(channel, lane, addresses, &lower) < 0)
|
||||||
|
die("Read training failure: lower bound.\n");
|
||||||
|
|
||||||
/*** Search upper bound. ***/
|
/*** Search upper bound. ***/
|
||||||
|
|
||||||
/* Start at lower + 1t. */
|
/* Start at lower + 1t. */
|
||||||
upper.t = lower.t + 1;
|
upper.t = lower.t + 1;
|
||||||
upper.p = lower.p;
|
upper.p = lower.p;
|
||||||
|
if (read_training_find_upper(channel, lane, addresses, &upper) < 0)
|
||||||
|
/* Overflow on upper edge is not fatal. */
|
||||||
|
printk(BIOS_WARNING, "Read training failure: upper bound.\n");
|
||||||
|
|
||||||
read_training_find_upper(channel, lane, addresses, &upper);
|
/*** Calculate and program mean value. ***/
|
||||||
|
|
||||||
/* Calculate and program mean value. */
|
|
||||||
lower.p += lower.t << READ_TIMING_P_SHIFT;
|
lower.p += lower.t << READ_TIMING_P_SHIFT;
|
||||||
upper.p += upper.t << READ_TIMING_P_SHIFT;
|
upper.p += upper.t << READ_TIMING_P_SHIFT;
|
||||||
const int mean_p = (lower.p + upper.p) >> 1;
|
const int mean_p = (lower.p + upper.p) >> 1;
|
||||||
|
Loading…
x
Reference in New Issue
Block a user