diff options
author | Nico Huber <nico.huber@secunet.com> | 2013-05-14 11:25:59 +0200 |
---|---|---|
committer | Stefan Reinauer <stefan.reinauer@coreboot.org> | 2013-05-22 18:02:24 +0200 |
commit | 35e45c078054a7bd64ba54b8b84b07602e8dd4ff (patch) | |
tree | c657405df30ece9046db7ce71731838a14d4af62 /src/northbridge/intel/gm45 | |
parent | 26a64351234093fbeea6e776be8829eae012ce7f (diff) |
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>
Diffstat (limited to 'src/northbridge/intel/gm45')
-rw-r--r-- | src/northbridge/intel/gm45/raminit_read_write_training.c | 88 |
1 files changed, 60 insertions, 28 deletions
diff --git a/src/northbridge/intel/gm45/raminit_read_write_training.c b/src/northbridge/intel/gm45/raminit_read_write_training.c index 6936d0788f..f13cde8e93 100644 --- a/src/northbridge/intel/gm45/raminit_read_write_training.c +++ b/src/northbridge/intel/gm45/raminit_read_write_training.c @@ -56,7 +56,7 @@ typedef struct { int t; int p; } 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) { timing->t++; @@ -66,18 +66,33 @@ static void normalize_read_timing(read_timing_t *const timing) timing->t--; timing->p += READ_TIMING_P_BOUND; } - if ((timing->t < 0) || (timing->t >= READ_TIMING_T_BOUND)) - die("Timing under-/overflow during read training.\n"); + if (timing->t < 0) { + 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, - read_timing_t *const timing) +static int program_read_timing(const int ch, const int lane, + read_timing_t *const timing) { - normalize_read_timing(timing); + if (normalize_read_timing(timing) < 0) + return -1; u32 reg = MCHBAR32(CxRDTy_MCHBAR(ch, lane)); reg &= ~(CxRDTy_T_MASK | CxRDTy_P_MASK); reg |= CxRDTy_T(timing->t) | CxRDTy_P(timing->p); MCHBAR32(CxRDTy_MCHBAR(ch, lane)) = reg; + + return 0; } /* Returns 1 on success, 0 on failure. */ 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; } -static void read_training_find_lower(const int channel, const int lane, - const address_bunch_t *const addresses, - read_timing_t *const lower) +static int read_training_find_lower(const int channel, const int lane, + const address_bunch_t *const addresses, + read_timing_t *const lower) { /* Coarse search for good t. */ program_read_timing(channel, lane, lower); while (!read_training_test(channel, lane, addresses)) { ++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. */ - if (lower->t > 0) { - --lower->t; - program_read_timing(channel, lane, lower); - while (!read_training_test(channel, lane, addresses)) { - ++lower->p; - program_read_timing(channel, lane, lower); - } + if (lower->t <= 0) + /* Can't step back, zero is good. */ + return 0; + + --lower->t; + 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, - const address_bunch_t *const addresses, - read_timing_t *const upper) +static int read_training_find_upper(const int channel, const int lane, + const address_bunch_t *const addresses, + read_timing_t *const upper) { - program_read_timing(channel, lane, upper); - if (!read_training_test(channel, lane, addresses)) - die("Read training failed: limits too narrow.\n"); + if (program_read_timing(channel, lane, upper) < 0) + return -1; + if (!read_training_test(channel, lane, addresses)) { + printk(BIOS_WARNING, + "Read training failure: limits too narrow.\n"); + return -1; + } /* Coarse search for bad t. */ do { ++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)); /* Fine search for bad p. */ --upper->t; program_read_timing(channel, lane, upper); while (read_training_test(channel, lane, addresses)) { ++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, 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. */ lower.t = 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. ***/ /* Start at lower + 1t. */ upper.t = lower.t + 1; 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; upper.p += upper.t << READ_TIMING_P_SHIFT; const int mean_p = (lower.p + upper.p) >> 1; |