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>
This commit is contained in:
parent
e464ccd116
commit
6d7a8c1125
|
@ -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
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -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__
|
||||
|
|
Loading…
Reference in New Issue