diff options
author | Arthur Heymans <arthur@aheymans.xyz> | 2017-03-07 20:48:14 +0100 |
---|---|---|
committer | Arthur Heymans <arthur@aheymans.xyz> | 2017-08-20 13:36:03 +0000 |
commit | 6d7a8c1125d17781fe2354eb316df247c82df741 (patch) | |
tree | 4b3522a81a6cc4ad9f11319301023aab80aa282e | |
parent | e464ccd116fe51137d9068c5db2edd7275ae8c9d (diff) | |
download | coreboot-6d7a8c1125d17781fe2354eb316df247c82df741.tar.xz |
nb/intel/x4x/raminit: Rework receive enable calibration
Moves receive enable calibration to a separate file to lighten
raminit.c a bit.
Receive enable calibration is quite similar to gm45 so it reuses some
of its function names.
The functional changes are:
* the minimum coarse is now reset for each channel;
* on the second fine search for DQS high, TAP overflow is handled by
increasing medium;
* start coarse at CAS + 1 instead of CAS - 1. Other Intel northbridges
do the same and the results are more in line with register dumps
from vendor bios.
These might improve stability.
TESTED on ga-g41m-es2l
Change-Id: I0c970455e609d3ce96a262cbf110336a2079da4d
Signed-off-by: Arthur Heymans <arthur@aheymans.xyz>
Reviewed-on: https://review.coreboot.org/18692
Tested-by: build bot (Jenkins) <no-reply@coreboot.org>
Reviewed-by: Nico Huber <nico.h@gmx.de>
-rw-r--r-- | src/northbridge/intel/x4x/Makefile.inc | 1 | ||||
-rw-r--r-- | src/northbridge/intel/x4x/raminit_ddr2.c | 283 | ||||
-rw-r--r-- | src/northbridge/intel/x4x/rcven.c | 375 | ||||
-rw-r--r-- | src/northbridge/intel/x4x/x4x.h | 1 |
4 files changed, 378 insertions, 282 deletions
diff --git a/src/northbridge/intel/x4x/Makefile.inc b/src/northbridge/intel/x4x/Makefile.inc index 34d9b0fdd7..5c64ca7e49 100644 --- a/src/northbridge/intel/x4x/Makefile.inc +++ b/src/northbridge/intel/x4x/Makefile.inc @@ -20,6 +20,7 @@ romstage-y += early_init.c romstage-y += raminit.c romstage-y += raminit_ddr2.c romstage-y += ram_calc.c +romstage-y += rcven.c ramstage-y += acpi.c ramstage-y += ram_calc.c diff --git a/src/northbridge/intel/x4x/raminit_ddr2.c b/src/northbridge/intel/x4x/raminit_ddr2.c index bc012d259b..9c13fc1995 100644 --- a/src/northbridge/intel/x4x/raminit_ddr2.c +++ b/src/northbridge/intel/x4x/raminit_ddr2.c @@ -31,11 +31,6 @@ #define ME_UMA_SIZEMB 0 -static inline void barrier(void) -{ - asm volatile("mfence":::); -} - static u32 fsb2mhz(u32 speed) { return (speed * 267) + 800; @@ -1161,282 +1156,6 @@ static void jedec_ddr2(struct sysinfo *s) printk(BIOS_DEBUG, "MRS done\n"); } -static u8 sampledqs(u16 mchloc, u32 addr, u8 hilow, u8 repeat) -{ - u8 dqsmatch = 1; - volatile u32 strobe; - - while (repeat-- > 0) { - MCHBAR8(0x5d8) = MCHBAR8(0x5d8) & ~0x2; - udelay(2); - MCHBAR8(0x5d8) = MCHBAR8(0x5d8) | 0x2; - udelay(2); - MCHBAR8(0x9d8) = MCHBAR8(0x9d8) & ~0x2; - udelay(2); - MCHBAR8(0x9d8) = MCHBAR8(0x9d8) | 0x2; - udelay(2); - barrier(); - strobe = read32((u32 *)addr); - barrier(); - if (((MCHBAR32(mchloc) & 0x40) >> 6) != hilow) - dqsmatch = 0; - } - return dqsmatch; -} - -static void rcven_ddr2(struct sysinfo *s) -{ - u8 i, reg8, ch, lane; - u32 addr; - u8 tap = 0; - u8 savecc, savemedium, savetap, coarsecommon, medium; - u8 lanecoarse[8] = {0}; - u8 mincoarse = 0xff; - u8 pitap[2][8]; - u16 coarsectrl[2]; - u16 coarsedelay[2]; - u16 mediumphase[2]; - u16 readdelay[2]; - u16 mchbar; - MCHBAR8(0x5d8) = MCHBAR8(0x5d8) & ~0xc; - MCHBAR8(0x9d8) = MCHBAR8(0x9d8) & ~0xc; - MCHBAR8(0x5dc) = MCHBAR8(0x5dc) & ~0x80; - - FOR_EACH_POPULATED_CHANNEL(s->dimms, ch) { - addr = (ch << 29); - for (i = 0; !RANK_IS_POPULATED(s->dimms, ch, i); i++) - addr += 128*1024*1024; - - for (lane = 0; lane < 8; lane++) { - printk(BIOS_DEBUG, "Channel %d, Lane %d addr=0x%08x\n", ch, lane, addr); - coarsecommon = (s->selected_timings.CAS - 1); - switch (lane) { - case 0: case 1: - medium = 0; - break; - case 2: case 3: - medium = 1; - break; - case 4: case 5: - medium = 2; - break; - case 6: case 7: - medium = 3; - break; - default: - medium = 0; - break; - } - mchbar = 0x400*ch + 0x561 + (lane << 2); - tap = 0; - MCHBAR32(0x400*ch + 0x248) = (MCHBAR32(0x400*ch + 0x248) & ~0xf0000) | - (coarsecommon << 16); - MCHBAR16(0x400*ch + 0x58c) = (MCHBAR16(0x400*ch + 0x58c) & ~(3 << (lane*2))) | - (medium << (lane*2)); - MCHBAR8(0x400*ch + 0x560 + lane*4) = MCHBAR8(0x400*ch + 0x560 + lane*4) & ~0xf; - MCHBAR8(0x400*ch + 0x560 + lane*4) = MCHBAR8(0x400*ch + 0x560 + lane*4) & ~0x70; - savecc = coarsecommon; - savemedium = medium; - savetap = 0; - - MCHBAR16(0x400*ch + 0x588) = (MCHBAR16(0x400*ch + 0x588) & ~(3 << (lane*2))) | - (1 << (lane*2)); - - printk(BIOS_DEBUG, "rcven 0.1 coarse=%d\n", coarsecommon); - while (sampledqs(mchbar, addr, 1, 1) == 1) { - if (medium < 3) { - medium++; - MCHBAR16(0x400*ch + 0x58c) = (MCHBAR16(0x400*ch + 0x58c) & - ~(3 << (lane*2))) | (medium << (lane*2)); - } else { - medium = 0; - coarsecommon++; - MCHBAR32(0x400*ch + 0x248) = (MCHBAR32(0x400*ch + 0x248) & - ~0xf0000) | (coarsecommon << 16); - MCHBAR16(0x400*ch + 0x58c) = (MCHBAR16(0x400*ch + 0x58c) & - ~(3 << (lane*2))) | (medium << (lane*2)); - } - if (coarsecommon > 16) { - die("Coarse > 16: DQS tuning failed, halt\n"); - break; - } - } - printk(BIOS_DEBUG, " GOT IT (high -> low transition) coarse=%d medium=%d\n", coarsecommon, medium); - - savemedium = medium; - savecc = coarsecommon; - if (medium < 3) { - medium++; - MCHBAR16(0x400*ch + 0x58c) = (MCHBAR16(0x400*ch + 0x58c) & - ~(3 << (lane*2))) | (medium << (lane*2)); - } else { - medium = 0; - coarsecommon++; - - MCHBAR32(0x400*ch + 0x248) = (MCHBAR32(0x400*ch + 0x248) & ~0xf0000) | - (coarsecommon << 16); - MCHBAR16(0x400*ch + 0x58c) = (MCHBAR16(0x400*ch + 0x58c) & ~(3 << (lane*2))) | - (medium << (lane*2)); - } - - printk(BIOS_DEBUG, "rcven 0.2\n"); - while (sampledqs(mchbar, addr, 0, 1) == 1) { - savemedium = medium; - savecc = coarsecommon; - if (medium < 3) { - medium++; - MCHBAR16(0x400*ch + 0x58c) = (MCHBAR16(0x400*ch + 0x58c) & - ~(3 << (lane*2))) | (medium << (lane*2)); - } else { - medium = 0; - coarsecommon++; - MCHBAR32(0x400*ch + 0x248) = (MCHBAR32(0x400*ch + 0x248) & - ~0xf0000) | (coarsecommon << 16); - MCHBAR16(0x400*ch + 0x58c) = (MCHBAR16(0x400*ch + 0x58c) & - ~(3 << (lane*2))) | (medium << (lane*2)); - } - if (coarsecommon > 16) { - die("Coarse DQS tuning 2 failed, halt\n"); - break; - } - } - printk(BIOS_DEBUG, " GOT IT (low -> high transition) coarse=%d medium=%d\n", coarsecommon, medium); - - - coarsecommon = savecc; - medium = savemedium; - MCHBAR32(0x400*ch + 0x248) = (MCHBAR32(0x400*ch + 0x248) & - ~0xf0000) | (coarsecommon << 16); - MCHBAR16(0x400*ch + 0x58c) = (MCHBAR16(0x400*ch + 0x58c) & - ~(3 << (lane*2))) | (medium << (lane*2)); - - printk(BIOS_DEBUG, "rcven 0.3\n"); - tap = 0; - while (sampledqs(mchbar, addr, 1, 1) == 0) { - savetap = tap; - tap++; - if (tap > 14) - break; - MCHBAR8(0x400*ch + 0x560 + (lane*4)) = - (MCHBAR8(0x400*ch + 0x560 + (lane*4)) & ~0xf) | tap; - } - - tap = savetap; - MCHBAR8(0x400*ch + 0x560 + (lane*4)) = - (MCHBAR8(0x400*ch + 0x560 + (lane*4)) & ~0xf) | tap; - MCHBAR8(0x400*ch + 0x560 + (lane*4)) = - (MCHBAR8(0x400*ch + 0x560 + (lane*4)) & ~0x70) | 0x30; - if (medium < 3) { - medium++; - MCHBAR16(0x400*ch + 0x58c) = (MCHBAR16(0x400*ch + 0x58c) & - ~(3 << (lane*2))) | (medium << (lane*2)); - } else { - medium = 0; - coarsecommon++; - MCHBAR32(0x400*ch + 0x248) = (MCHBAR32(0x400*ch + 0x248) & - ~0xf0000) | (coarsecommon << 16); - MCHBAR16(0x400*ch + 0x58c) = (MCHBAR16(0x400*ch + 0x58c) & - ~(3 << (lane*2))) | (medium << (lane*2)); - } - if (sampledqs(mchbar, addr, 1, 1) == 0) - die("Not at DQS high, doh\n"); - - printk(BIOS_DEBUG, "rcven 0.4\n"); - while (sampledqs(mchbar, addr, 1, 1) == 1) { - coarsecommon--; - MCHBAR32(0x400*ch + 0x248) = (MCHBAR32(0x400*ch + 0x248) & - ~0xf0000) | (coarsecommon << 16); - if (coarsecommon == 0) { - die("Couldn't find DQS-high 0 indicator, halt\n"); - break; - } - } - printk(BIOS_DEBUG, " GOT IT (high -> low transition) coarse=%d medium=%d\n", coarsecommon, medium); - - printk(BIOS_DEBUG, "rcven 0.5\n"); - while (sampledqs(mchbar, addr, 0, 1) == 1) { - savemedium = medium; - savecc = coarsecommon; - if (medium < 3) { - medium++; - MCHBAR16(0x400*ch + 0x58c) = (MCHBAR16(0x400*ch + 0x58c) & - ~(3 << (lane*2))) | (medium << (lane*2)); - } else { - medium = 0; - coarsecommon++; - MCHBAR32(0x400*ch + 0x248) = (MCHBAR32(0x400*ch + 0x248) & - ~0xf0000) | (coarsecommon << 16); - MCHBAR16(0x400*ch + 0x58c) = (MCHBAR16(0x400*ch + 0x58c) & - ~(3 << (lane*2))) | (medium << (lane*2)); - } - if (coarsecommon > 16) { - die("Coarse DQS tuning 5 failed, halt\n"); - break; - } - } - printk(BIOS_DEBUG, " GOT IT (low -> high transition) coarse=%d medium=%d\n", coarsecommon, medium); - - printk(BIOS_DEBUG, "rcven 0.6\n"); - coarsecommon = savecc; - medium = savemedium; - MCHBAR32(0x400*ch + 0x248) = (MCHBAR32(0x400*ch + 0x248) & - ~0xf0000) | (coarsecommon << 16); - MCHBAR16(0x400*ch + 0x58c) = (MCHBAR16(0x400*ch + 0x58c) & - ~(3 << (lane*2))) | (medium << (lane*2)); - while (sampledqs(mchbar, addr, 1, 1) == 0) { - savetap = tap; - tap++; - if (tap > 14) - break; - MCHBAR8(0x400*ch + 0x560 + lane*4) = - (MCHBAR8(0x400*ch + 0x560 + lane*4) & ~0xf) | tap; - } - tap = savetap; - MCHBAR8(0x400*ch + 0x560 + lane*4) = - (MCHBAR8(0x400*ch + 0x560 + lane*4) & ~0xf) | tap; - MCHBAR8(0x400*ch + 0x560 + lane*4) = - (MCHBAR8(0x400*ch + 0x560 + lane*4) & ~0x70) | 0x70; - - pitap[ch][lane] = 0x70 | tap; - - MCHBAR16(0x400*ch + 0x588) = MCHBAR16(0x400*ch + 0x588) & ~(3 << (lane*2)); - lanecoarse[lane] = coarsecommon; - printk(BIOS_DEBUG, "rcven 0.7\n"); - } // END EACH LANE - - // Find minimum coarse value - for (lane = 0; lane < 8; lane++) { - if (mincoarse > lanecoarse[lane]) - mincoarse = lanecoarse[lane]; - } - - printk(BIOS_DEBUG, "Found min coarse value = %d\n", mincoarse); - - for (lane = 0; lane < 8; lane++) { - reg8 = (lanecoarse[lane] == 0) ? 0 : lanecoarse[lane] - mincoarse; - MCHBAR16(0x400*ch + 0x5fa) = (MCHBAR16(0x400*ch + 0x5fa) & ~(3 << (lane*2))) | - (reg8 << (lane*2)); - } - MCHBAR32(0x400*ch + 0x248) = (MCHBAR32(0x400*ch + 0x248) & ~0xf0000) | (mincoarse << 16); - coarsectrl[ch] = mincoarse; - coarsedelay[ch] = MCHBAR16(0x400*ch + 0x5fa); - mediumphase[ch] = MCHBAR16(0x400*ch + 0x58c); - readdelay[ch] = MCHBAR16(0x400*ch + 0x588); - } // END EACH POPULATED CHANNEL - - FOR_EACH_CHANNEL(ch) { - for (lane = 0; lane < 8; lane++) { - MCHBAR8(0x400*ch + 0x560 + (lane*4)) = - (MCHBAR8(0x400*ch + 0x560 + (lane*4)) & ~0xf) | pitap[ch][lane]; - } - MCHBAR32(0x400*ch + 0x248) = (MCHBAR32(0x400*ch + 0x248) & ~0xf0000) | - (coarsectrl[ch] << 16); - MCHBAR16(0x400*ch + 0x5fa) = coarsedelay[ch]; - MCHBAR16(0x400*ch + 0x58c) = mediumphase[ch]; - } - printk(BIOS_DEBUG, "End rcven\n"); -} - static void sdram_save_receive_enable(void) { int i = 0; @@ -1505,7 +1224,7 @@ static void sdram_program_receive_enable(struct sysinfo *s) || (s->boot_path == BOOT_PATH_RESUME)) { sdram_recover_receive_enable(); } else { - rcven_ddr2(s); + rcven(s); sdram_save_receive_enable(); } } diff --git a/src/northbridge/intel/x4x/rcven.c b/src/northbridge/intel/x4x/rcven.c new file mode 100644 index 0000000000..23f8d52c3c --- /dev/null +++ b/src/northbridge/intel/x4x/rcven.c @@ -0,0 +1,375 @@ +/* + * This file is part of the coreboot project. + * + * Copyright (C) 2015 Damien Zammit <damien@zamaudio.com> + * Copyright (C) 2017 Arthur Heymans <arthur@aheymans.xyz> + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include <arch/io.h> +#include <console/console.h> +#include <delay.h> +#include "iomap.h" +#include "x4x.h" + +#define MAX_COARSE 15 +#define DQS_HIGH 1 +#define DQS_LOW 0 + +#define RESET_CNTL(channel) (0x5d8 + channel * 0x400) + +struct rec_timing { + u8 medium; + u8 coarse; + u8 pi; + u8 tap; +}; + +static inline void barrier(void) +{ + asm volatile("mfence":::); +} + +static u8 sampledqs(u32 addr, u8 lane, u8 channel) +{ + volatile u32 strobe; + u32 sample_offset = 0x400 * channel + 0x561 + lane * 4; + + /* Reset the DQS probe */ + MCHBAR8(RESET_CNTL(channel)) &= ~0x2; + udelay(2); + MCHBAR8(RESET_CNTL(channel)) |= 0x2; + udelay(2); + barrier(); + strobe = read32((u32 *)addr); + barrier(); + return (MCHBAR8(sample_offset) >> 6) & 1; +} + +static void program_timing(const struct rec_timing *timing, u8 channel, + u8 lane) +{ + u32 reg32; + u16 reg16; + u8 reg8; + + printk(RAM_SPEW, " Programming timings:" + "Coarse: %d, Medium: %d, TAP: %d, PI: %d\n", + timing->coarse, timing->medium, timing->tap, timing->pi); + + reg32 = MCHBAR32(0x400 * channel + 0x248); + reg32 &= ~0xf0000; + reg32 |= timing->coarse << 16; + MCHBAR32(0x400 * channel + 0x248) = reg32; + + reg16 = MCHBAR16(0x400 * channel + 0x58c); + reg16 &= ~(3 << (lane * 2)); + reg16 |= timing->medium << (lane * 2); + MCHBAR16(0x400 * channel + 0x58c) = reg16; + + reg8 = MCHBAR8(0x400 * channel + 0x560 + lane * 4); + reg8 &= ~0x7f; + reg8 |= timing->tap; + reg8 |= timing->pi << 4; + MCHBAR8(0x400 * channel + 0x560 + lane * 4) = reg8; +} + +static int increase_medium(struct rec_timing *timing) +{ + if (timing->medium < 3) { + timing->medium++; + } else if (timing->coarse < MAX_COARSE) { + timing->medium = 0; + timing->coarse++; + } else { + printk(BIOS_ERR, "Cannot increase medium any further.\n"); + return -1; + } + return 0; +} + +static int decrease_medium(struct rec_timing *timing) +{ + if (timing->medium > 0) { + timing->medium--; + } else if (timing->coarse > 0) { + timing->medium = 3; + timing->coarse--; + } else { + printk(BIOS_ERR, "Cannot lower medium any further.\n"); + return -1; + } + return 0; +} + +static int increase_tap(struct rec_timing *timing) +{ + if (timing->tap == 14) { + if (increase_medium(timing)) + return -1; + timing->tap = 0; + } else { + timing->tap++; + } + return 0; +} + +static int decrease_tap(struct rec_timing *timing) +{ + if (timing->tap > 0) { + timing->tap--; + } else { + if (decrease_medium(timing)) + return -1; + timing->tap = 14; + } + return 0; +} + +static int decr_coarse_low(u8 channel, u8 lane, u32 addr, + struct rec_timing *timing) +{ + printk(BIOS_DEBUG, + " Decreasing coarse until high to low transition is found\n"); + while (sampledqs(addr, lane, channel) != DQS_LOW) { + if (timing->coarse == 0) { + printk(BIOS_CRIT, + "Couldn't find DQS-high 0 indicator, halt\n"); + return -1; + } + timing->coarse--; + program_timing(timing, channel, lane); + } + printk(BIOS_DEBUG, " DQS low at coarse=%d medium=%d\n", + timing->coarse, timing->medium); + return 0; +} + +static int fine_search_dqs_high(u8 channel, u8 lane, u32 addr, + struct rec_timing *timing) +{ + printk(BIOS_DEBUG, + " Increasing TAP until low to high transition is found\n"); + /* + * We use a do while loop since it happens that the strobe read + * is inconsistent, with the strobe already high. The current + * code flow results in failure later when finding the preamble, + * at which DQS needs to be high is often not the case if TAP was + * not increased at least once here. Work around this by incrementing + * TAP at least once to guarantee searching for preamble start at + * DQS high. + * This seems to be the result of hysteresis on some settings, where + * the DQS probe is influenced by its previous value. + */ + if (sampledqs(addr, lane, channel) == DQS_HIGH) { + printk(BIOS_WARNING, + "DQS already HIGH... DQS probe is inconsistent!\n" + "Continuing....\n"); + } + do { + if (increase_tap(timing)) { + printk(BIOS_CRIT, + "Could not find DQS-high on fine search.\n"); + return -1; + } + program_timing(timing, channel, lane); + } while (sampledqs(addr, lane, channel) != DQS_HIGH); + + printk(BIOS_DEBUG, " DQS high at coarse=%d medium=%d tap:%d\n", + timing->coarse, timing->medium, timing->tap); + return 0; +} + +static int find_dqs_low(u8 channel, u8 lane, u32 addr, + struct rec_timing *timing) +{ + /* Look for DQS low, using quarter steps. */ + printk(BIOS_DEBUG, " Increasing medium until DQS LOW is found\n"); + while (sampledqs(addr, lane, channel) != DQS_LOW) { + if (increase_medium(timing)) { + printk(BIOS_CRIT, + "Coarse > 15: DQS tuning failed, halt\n"); + return -1; + } + program_timing(timing, channel, lane); + } + printk(BIOS_DEBUG, " DQS low at coarse=%d medium=%d\n", + timing->coarse, timing->medium); + return 0; +} +static int find_dqs_high(u8 channel, u8 lane, u32 addr, + struct rec_timing *timing) +{ + /* Look for DQS high, using quarter steps. */ + printk(BIOS_DEBUG, " Increasing medium until DQS HIGH is found\n"); + while (sampledqs(addr, lane, channel) != DQS_HIGH) { + if (increase_medium(timing)) { + printk(BIOS_CRIT, + "Coarse > 16: DQS tuning failed, halt\n"); + return -1; + } + program_timing(timing, channel, lane); + } + printk(BIOS_DEBUG, " DQS high at coarse=%d medium=%d\n", + timing->coarse, timing->medium); + return 0; +} + +static int find_dqs_edge_lowhigh(u8 channel, u8 lane, + u32 addr, struct rec_timing *timing) +{ + /* Medium search for DQS high. */ + if (find_dqs_high(channel, lane, addr, timing)) + return -1; + + /* Go back and perform finer search. */ + if (decrease_medium(timing)) + return -1; + program_timing(timing, channel, lane); + if (fine_search_dqs_high(channel, lane, addr, timing) < 0) + return -1; + + return 0; +} + +static int find_preamble(u8 channel, u8 lane, u32 addr, + struct rec_timing *timing) +{ + /* Add a quarter step */ + if (increase_medium(timing)) + return -1; + program_timing(timing, channel, lane); + /* Verify we are at high */ + if (sampledqs(addr, lane, channel) != DQS_HIGH) { + printk(BIOS_CRIT, "Not at DQS high, d'oh\n"); + return -1; + } + + /* Decrease coarse until LOW is found */ + if (decr_coarse_low(channel, lane, addr, timing)) + return -1; + return 0; +} + +static int calibrate_receive_enable(u8 channel, u8 lane, + u32 addr, struct rec_timing *timing) +{ + program_timing(timing, channel, lane); + /* Set receive enable bit */ + MCHBAR16(0x400 * channel + 0x588) = (MCHBAR16(0x400 * channel + 0x588) + & ~(3 << (lane * 2))) | (1 << (lane * 2)); + + if (find_dqs_low(channel, lane, addr, timing)) + return -1; + + /* Advance a little further. */ + if (increase_medium(timing)) { + /* A finer search could be implemented */ + printk(BIOS_WARNING, "Cannot increase medium further"); + return -1; + } + program_timing(timing, channel, lane); + + if (find_dqs_edge_lowhigh(channel, lane, addr, timing)) + return -1; + + /* Go back on fine search */ + if (decrease_tap(timing)) + return -1; + timing->pi = 3; + program_timing(timing, channel, lane); + + if (find_preamble(channel, lane, addr, timing)) + return -1; + + if (find_dqs_edge_lowhigh(channel, lane, addr, timing)) + return -1; + if (decrease_tap(timing)) + return -1; + timing->pi = 7; + program_timing(timing, channel, lane); + + /* Unset receive enable bit */ + MCHBAR16(0x400 * channel + 0x588) = MCHBAR16(0x400 * channel + 0x588) & + ~(3 << (lane * 2)); + return 0; +} + +void rcven(const struct sysinfo *s) +{ + int i; + u8 channel, lane, reg8; + u32 addr; + struct rec_timing timing[8]; + u8 mincoarse; + + MCHBAR8(0x5d8) = MCHBAR8(0x5d8) & ~0xc; + MCHBAR8(0x9d8) = MCHBAR8(0x9d8) & ~0xc; + MCHBAR8(0x5dc) = MCHBAR8(0x5dc) & ~0x80; + FOR_EACH_POPULATED_CHANNEL(s->dimms, channel) { + addr = (channel << 29); + mincoarse = 0xff; + for (i = 0; i < RANKS_PER_CHANNEL && + !RANK_IS_POPULATED(s->dimms, channel, i); i++) + addr += 128 * MiB; + for (lane = 0; lane < 8; lane++) { + printk(BIOS_DEBUG, "Channel %d, Lane %d addr=0x%08x\n", + channel, lane, addr); + timing[lane].coarse = (s->selected_timings.CAS + 1); + switch (lane) { + default: + case 0: + case 1: + timing[lane].medium = 0; + break; + case 2: + case 3: + timing[lane].medium = 1; + break; + case 4: + case 5: + timing[lane].medium = 2; + break; + case 6: + case 7: + timing[lane].medium = 3; + break; + } + timing[lane].tap = 0; + timing[lane].pi = 0; + + if (calibrate_receive_enable(channel, lane, addr, + &timing[lane])) + die("Receive enable calibration failed\n"); + if (mincoarse > timing[lane].coarse) + mincoarse = timing[lane].coarse; + } + printk(BIOS_DEBUG, "Found min coarse value = %d\n", mincoarse); + printk(BIOS_DEBUG, "Receive enable, final timings:\n"); + /* Normalise coarse */ + for (lane = 0; lane < 8; lane++) { + if (timing[lane].coarse == 0) + reg8 = 0; + else + reg8 = timing[lane].coarse - mincoarse; + printk(BIOS_DEBUG, "ch %d lane %d: coarse offset: %d;" + "medium: %d; tap: %d\n", + channel, lane, reg8, timing[lane].medium, + timing[lane].tap); + MCHBAR16(0x400 * channel + 0x5fa) &= + ~(3 << (lane * 2)) | (reg8 << (lane * 2)); + } + /* simply use timing[0] to program mincoarse */ + timing[0].coarse = mincoarse; + program_timing(&timing[0], channel, 0); + } +} diff --git a/src/northbridge/intel/x4x/x4x.h b/src/northbridge/intel/x4x/x4x.h index 7d8f5cc442..9db6c12122 100644 --- a/src/northbridge/intel/x4x/x4x.h +++ b/src/northbridge/intel/x4x/x4x.h @@ -345,6 +345,7 @@ u32 decode_igd_gtt_size(u32 gsm); u8 decode_pciebar(u32 *const base, u32 *const len); void sdram_initialize(int boot_path, const u8 *spd_map); void raminit_ddr2(struct sysinfo *); +void rcven(const struct sysinfo *); struct acpi_rsdp; #ifndef __SIMPLE_DEVICE__ |