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:
Nico Huber 2013-05-14 11:25:59 +02:00 committed by Stefan Reinauer
parent 26a6435123
commit 35e45c0780

View File

@ -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;