aboutsummaryrefslogtreecommitdiff
path: root/src/northbridge
diff options
context:
space:
mode:
authorArthur Heymans <arthur@aheymans.xyz>2017-03-07 20:48:14 +0100
committerArthur Heymans <arthur@aheymans.xyz>2017-08-20 13:36:03 +0000
commit6d7a8c1125d17781fe2354eb316df247c82df741 (patch)
tree4b3522a81a6cc4ad9f11319301023aab80aa282e /src/northbridge
parente464ccd116fe51137d9068c5db2edd7275ae8c9d (diff)
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>
Diffstat (limited to 'src/northbridge')
-rw-r--r--src/northbridge/intel/x4x/Makefile.inc1
-rw-r--r--src/northbridge/intel/x4x/raminit_ddr2.c283
-rw-r--r--src/northbridge/intel/x4x/rcven.c375
-rw-r--r--src/northbridge/intel/x4x/x4x.h1
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__