nb/intel/x4x: Fix raminit on reset path
Previously the raminit failed on hot reset and to work around this issue it unconditionally did a cold reset. This has the following issues: * it's slow; * when the OS issues a hot reset some disk drives expect their 5V power supply to remain on, which gets cut off by a cold reset, causing data corruption. To fix this some steps in raminit must be ommited on the reset path. This includes receive enable calibration. To achieve this it stores receive enable results in RTC nvram for them to be rewritten on the resume path. Note: The same thing needs to be done on the S3 resume path. Calling a hot reset after raminit "outb(0x6, 0cf9)" works. Change-Id: I6601dd90aebd071a0de7cec070487b0f9845bc30 Signed-off-by: Arthur Heymans <arthur@aheymans.xyz> Reviewed-on: https://review.coreboot.org/18009 Tested-by: build bot (Jenkins) Reviewed-by: Nico Huber <nico.h@gmx.de>
This commit is contained in:
parent
17335fab17
commit
97e13d84c3
|
@ -68,6 +68,7 @@ entries
|
||||||
# coreboot config options: check sums
|
# coreboot config options: check sums
|
||||||
984 16 h 0 check_sum
|
984 16 h 0 check_sum
|
||||||
|
|
||||||
|
1024 144 r 0 recv_enable_results
|
||||||
# -----------------------------------------------------------------
|
# -----------------------------------------------------------------
|
||||||
|
|
||||||
enumerations
|
enumerations
|
||||||
|
|
|
@ -30,6 +30,7 @@
|
||||||
#include <lib.h>
|
#include <lib.h>
|
||||||
#include <arch/stages.h>
|
#include <arch/stages.h>
|
||||||
#include <cbmem.h>
|
#include <cbmem.h>
|
||||||
|
#include <northbridge/intel/x4x/iomap.h>
|
||||||
|
|
||||||
#define SERIAL_DEV PNP_DEV(0x2e, IT8718F_SP1)
|
#define SERIAL_DEV PNP_DEV(0x2e, IT8718F_SP1)
|
||||||
#define GPIO_DEV PNP_DEV(0x2e, IT8718F_GPIO)
|
#define GPIO_DEV PNP_DEV(0x2e, IT8718F_GPIO)
|
||||||
|
@ -130,6 +131,7 @@ void mainboard_romstage_entry(unsigned long bist)
|
||||||
{
|
{
|
||||||
// ch0 ch1
|
// ch0 ch1
|
||||||
const u8 spd_addrmap[4] = { 0x50, 0, 0x52, 0 };
|
const u8 spd_addrmap[4] = { 0x50, 0, 0x52, 0 };
|
||||||
|
u8 boot_path = 0;
|
||||||
|
|
||||||
/* Disable watchdog timer */
|
/* Disable watchdog timer */
|
||||||
RCBA32(0x3410) = RCBA32(0x3410) | 0x20;
|
RCBA32(0x3410) = RCBA32(0x3410) | 0x20;
|
||||||
|
@ -149,8 +151,11 @@ void mainboard_romstage_entry(unsigned long bist)
|
||||||
|
|
||||||
x4x_early_init();
|
x4x_early_init();
|
||||||
|
|
||||||
|
if (MCHBAR32(PMSTS_MCHBAR) & PMSTS_WARM_RESET)
|
||||||
|
boot_path = BOOT_PATH_WARM_RESET;
|
||||||
|
|
||||||
printk(BIOS_DEBUG, "Initializing memory\n");
|
printk(BIOS_DEBUG, "Initializing memory\n");
|
||||||
sdram_initialize(0, spd_addrmap);
|
sdram_initialize(boot_path, spd_addrmap);
|
||||||
quick_ram_check();
|
quick_ram_check();
|
||||||
cbmem_initialize_empty();
|
cbmem_initialize_empty();
|
||||||
printk(BIOS_DEBUG, "Memory initialized\n");
|
printk(BIOS_DEBUG, "Memory initialized\n");
|
||||||
|
|
|
@ -20,6 +20,11 @@
|
||||||
#include <console/console.h>
|
#include <console/console.h>
|
||||||
#include <commonlib/helpers.h>
|
#include <commonlib/helpers.h>
|
||||||
#include <delay.h>
|
#include <delay.h>
|
||||||
|
#include <pc80/mc146818rtc.h>
|
||||||
|
/* This northbridge can also occur with ICH10 */
|
||||||
|
#if IS_ENABLED(CONFIG_SOUTHBRIDGE_INTEL_I82801GX)
|
||||||
|
#include <southbridge/intel/i82801gx/i82801gx.h>
|
||||||
|
#endif
|
||||||
#include "iomap.h"
|
#include "iomap.h"
|
||||||
#include "x4x.h"
|
#include "x4x.h"
|
||||||
|
|
||||||
|
@ -257,26 +262,25 @@ static void clkcross_ddr2(struct sysinfo *s)
|
||||||
static void checkreset_ddr2(struct sysinfo *s)
|
static void checkreset_ddr2(struct sysinfo *s)
|
||||||
{
|
{
|
||||||
u8 pmcon2;
|
u8 pmcon2;
|
||||||
u8 reset = 0;
|
|
||||||
|
|
||||||
pmcon2 = pci_read_config8(PCI_DEV(0, 0x1f, 0), 0xa2);
|
pmcon2 = pci_read_config8(PCI_DEV(0, 0x1f, 0), 0xa2);
|
||||||
if (!(pmcon2 & 0x80)) {
|
|
||||||
pmcon2 |= 0x80;
|
if (pmcon2 & 0x80) {
|
||||||
|
pmcon2 &= ~0x80;
|
||||||
pci_write_config8(PCI_DEV(0, 0x1f, 0), 0xa2, pmcon2);
|
pci_write_config8(PCI_DEV(0, 0x1f, 0), 0xa2, pmcon2);
|
||||||
reset = 1;
|
|
||||||
|
|
||||||
/* do magic 0xf0 thing. */
|
/* do magic 0xf0 thing. */
|
||||||
u8 reg8 = pci_read_config8(PCI_DEV(0, 0, 0), 0xf0);
|
u8 reg8 = pci_read_config8(PCI_DEV(0, 0, 0), 0xf0);
|
||||||
pci_write_config8(PCI_DEV(0, 0, 0), 0xf0, reg8 & ~(1 << 2));
|
pci_write_config8(PCI_DEV(0, 0, 0), 0xf0, reg8 & ~(1 << 2));
|
||||||
reg8 = pci_read_config8(PCI_DEV(0, 0, 0), 0xf0);
|
reg8 = pci_read_config8(PCI_DEV(0, 0, 0), 0xf0);
|
||||||
pci_write_config8(PCI_DEV(0, 0, 0), 0xf0, reg8 | (1 << 2));
|
pci_write_config8(PCI_DEV(0, 0, 0), 0xf0, reg8 | (1 << 2));
|
||||||
}
|
|
||||||
if (reset) {
|
|
||||||
printk(BIOS_DEBUG, "Reset...\n");
|
printk(BIOS_DEBUG, "Reset...\n");
|
||||||
outb(0xe, 0xcf9);
|
outb(0x6, 0xcf9);
|
||||||
asm ("hlt");
|
asm ("hlt");
|
||||||
}
|
}
|
||||||
pci_write_config8(PCI_DEV(0, 0x1f, 0), 0xa2, pmcon2 | 0x80);
|
pmcon2 |= 0x80;
|
||||||
|
pci_write_config8(PCI_DEV(0, 0x1f, 0), 0xa2, pmcon2);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void setioclk_ddr2(struct sysinfo *s)
|
static void setioclk_ddr2(struct sysinfo *s)
|
||||||
|
@ -1490,6 +1494,78 @@ static void rcven_ddr2(struct sysinfo *s)
|
||||||
printk(BIOS_DEBUG, "End rcven\n");
|
printk(BIOS_DEBUG, "End rcven\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void sdram_save_receive_enable(void)
|
||||||
|
{
|
||||||
|
int i = 0;
|
||||||
|
u16 reg16;
|
||||||
|
u8 values[18];
|
||||||
|
u8 lane, ch;
|
||||||
|
|
||||||
|
FOR_EACH_CHANNEL(ch) {
|
||||||
|
lane = 0;
|
||||||
|
while (lane < 8) {
|
||||||
|
values[i] = (MCHBAR8(0x400*ch + 0x560 + lane++ * 4) & 0xf);
|
||||||
|
values[i++] |= (MCHBAR8(0x400*ch + 0x560 + lane++ * 4) & 0xf) << 4;
|
||||||
|
}
|
||||||
|
values[i++] = (MCHBAR32(0x400*ch + 0x248) >> 16) & 0xf;
|
||||||
|
reg16 = MCHBAR16(0x400*ch + 0x5fa);
|
||||||
|
values[i++] = reg16 & 0xff;
|
||||||
|
values[i++] = (reg16 >> 8) & 0xff;
|
||||||
|
reg16 = MCHBAR16(0x400*ch + 0x58c);
|
||||||
|
values[i++] = reg16 & 0xff;
|
||||||
|
values[i++] = (reg16 >> 8) & 0xff;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (i = 0; i < ARRAY_SIZE(values); i++)
|
||||||
|
cmos_write(values[i], 128 + i);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void sdram_recover_receive_enable(void)
|
||||||
|
{
|
||||||
|
u8 i;
|
||||||
|
u32 reg32;
|
||||||
|
u16 reg16;
|
||||||
|
u8 values[18];
|
||||||
|
u8 ch, lane;
|
||||||
|
|
||||||
|
for (i = 0; i < ARRAY_SIZE(values); i++)
|
||||||
|
values[i] = cmos_read(128 + i);
|
||||||
|
|
||||||
|
i = 0;
|
||||||
|
FOR_EACH_CHANNEL(ch) {
|
||||||
|
lane = 0;
|
||||||
|
while (lane < 8) {
|
||||||
|
MCHBAR8(0x400*ch + 0x560 + lane++ * 4) = 0x70 |
|
||||||
|
(values[i] & 0xf);
|
||||||
|
MCHBAR8(0x400*ch + 0x560 + lane++ * 4) = 0x70 |
|
||||||
|
((values[i++] >> 4) & 0xf);
|
||||||
|
}
|
||||||
|
reg32 = (MCHBAR32(0x400*ch + 0x248) & ~0xf0000)
|
||||||
|
| ((values[i++] & 0xf) << 16);
|
||||||
|
MCHBAR32(0x400*ch + 0x248) = reg32;
|
||||||
|
reg16 = values[i++];
|
||||||
|
reg16 |= values[i++] << 8;
|
||||||
|
MCHBAR16(0x400*ch + 0x5fa) = reg16;
|
||||||
|
reg16 = values[i++];
|
||||||
|
reg16 |= values[i++] << 8;
|
||||||
|
MCHBAR16(0x400*ch + 0x58c) = reg16;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void sdram_program_receive_enable(struct sysinfo *s)
|
||||||
|
{
|
||||||
|
/* enable upper CMOS */
|
||||||
|
RCBA32(0x3400) = (1 << 2);
|
||||||
|
|
||||||
|
/* Program Receive Enable Timings */
|
||||||
|
if (s->boot_path == BOOT_PATH_WARM_RESET) {
|
||||||
|
sdram_recover_receive_enable();
|
||||||
|
} else {
|
||||||
|
rcven_ddr2(s);
|
||||||
|
sdram_save_receive_enable();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
static void dradrb_ddr2(struct sysinfo *s)
|
static void dradrb_ddr2(struct sysinfo *s)
|
||||||
{
|
{
|
||||||
u8 map, i, ch, r, rankpop0, rankpop1;
|
u8 map, i, ch, r, rankpop0, rankpop1;
|
||||||
|
@ -1863,23 +1939,25 @@ void raminit_ddr2(struct sysinfo *s)
|
||||||
// Reset if required
|
// Reset if required
|
||||||
checkreset_ddr2(s);
|
checkreset_ddr2(s);
|
||||||
|
|
||||||
// Clear self refresh
|
if (s->boot_path != BOOT_PATH_WARM_RESET) {
|
||||||
MCHBAR32(0xf14) = MCHBAR32(0xf14) | 0x3;
|
// Clear self refresh
|
||||||
|
MCHBAR32(PMSTS_MCHBAR) = MCHBAR32(PMSTS_MCHBAR)
|
||||||
|
| PMSTS_BOTH_SELFREFRESH;
|
||||||
|
|
||||||
// Clear host clk gate reg
|
// Clear host clk gate reg
|
||||||
MCHBAR32(0x1c) = MCHBAR32(0x1c) | 0xffffffff;
|
MCHBAR32(0x1c) = MCHBAR32(0x1c) | 0xffffffff;
|
||||||
|
|
||||||
// Select DDR2
|
// Select DDR2
|
||||||
MCHBAR8(0x1a8) = MCHBAR8(0x1a8) & ~0x4;
|
MCHBAR8(0x1a8) = MCHBAR8(0x1a8) & ~0x4;
|
||||||
|
|
||||||
// Set freq
|
// Set freq
|
||||||
MCHBAR32(0xc00) = (MCHBAR32(0xc00) & ~0x70) |
|
MCHBAR32(0xc00) = (MCHBAR32(0xc00) & ~0x70) |
|
||||||
(s->selected_timings.mem_clk << 4) | (1 << 10);
|
(s->selected_timings.mem_clk << 4) | (1 << 10);
|
||||||
|
|
||||||
// Overwrite freq if chipset rejects it
|
// Overwrite freq if chipset rejects it
|
||||||
s->selected_timings.mem_clk = (MCHBAR8(0xc00) & 0x70) >> 4;
|
s->selected_timings.mem_clk = (MCHBAR8(0xc00) & 0x70) >> 4;
|
||||||
if (s->selected_timings.mem_clk > (s->max_fsb + 3)) {
|
if (s->selected_timings.mem_clk > (s->max_fsb + 3))
|
||||||
die("Error: DDR is faster than FSB, halt\n");
|
die("Error: DDR is faster than FSB, halt\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
udelay(250000);
|
udelay(250000);
|
||||||
|
@ -1889,8 +1967,10 @@ void raminit_ddr2(struct sysinfo *s)
|
||||||
printk(BIOS_DEBUG, "Done clk crossing\n");
|
printk(BIOS_DEBUG, "Done clk crossing\n");
|
||||||
|
|
||||||
// DDR2 IO
|
// DDR2 IO
|
||||||
setioclk_ddr2(s);
|
if (s->boot_path != BOOT_PATH_WARM_RESET) {
|
||||||
printk(BIOS_DEBUG, "Done I/O clk\n");
|
setioclk_ddr2(s);
|
||||||
|
printk(BIOS_DEBUG, "Done I/O clk\n");
|
||||||
|
}
|
||||||
|
|
||||||
// Grant to launch
|
// Grant to launch
|
||||||
launch_ddr2(s);
|
launch_ddr2(s);
|
||||||
|
@ -1904,16 +1984,21 @@ void raminit_ddr2(struct sysinfo *s)
|
||||||
dll_ddr2(s);
|
dll_ddr2(s);
|
||||||
|
|
||||||
// RCOMP
|
// RCOMP
|
||||||
rcomp_ddr2(s);
|
if (s->boot_path != BOOT_PATH_WARM_RESET) {
|
||||||
printk(BIOS_DEBUG, "RCOMP\n");
|
rcomp_ddr2(s);
|
||||||
|
printk(BIOS_DEBUG, "RCOMP\n");
|
||||||
|
}
|
||||||
|
|
||||||
// ODT
|
// ODT
|
||||||
odt_ddr2(s);
|
odt_ddr2(s);
|
||||||
printk(BIOS_DEBUG, "Done ODT\n");
|
printk(BIOS_DEBUG, "Done ODT\n");
|
||||||
|
|
||||||
// RCOMP update
|
// RCOMP update
|
||||||
while ((MCHBAR8(0x130) & 1) != 0 );
|
if (s->boot_path != BOOT_PATH_WARM_RESET) {
|
||||||
printk(BIOS_DEBUG, "Done RCOMP update\n");
|
while ((MCHBAR8(0x130) & 1) != 0)
|
||||||
|
;
|
||||||
|
printk(BIOS_DEBUG, "Done RCOMP update\n");
|
||||||
|
}
|
||||||
|
|
||||||
// Set defaults
|
// Set defaults
|
||||||
MCHBAR32(0x260) = (MCHBAR32(0x260) & ~1) | 0xf00000;
|
MCHBAR32(0x260) = (MCHBAR32(0x260) & ~1) | 0xf00000;
|
||||||
|
@ -1993,7 +2078,7 @@ void raminit_ddr2(struct sysinfo *s)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Receive enable
|
// Receive enable
|
||||||
rcven_ddr2(s);
|
sdram_program_receive_enable(s);
|
||||||
printk(BIOS_DEBUG, "Done rcven\n");
|
printk(BIOS_DEBUG, "Done rcven\n");
|
||||||
|
|
||||||
// Finish rcven
|
// Finish rcven
|
||||||
|
@ -2008,16 +2093,23 @@ void raminit_ddr2(struct sysinfo *s)
|
||||||
MCHBAR8(0x5dc) = MCHBAR8(0x5dc) | 0x80;
|
MCHBAR8(0x5dc) = MCHBAR8(0x5dc) | 0x80;
|
||||||
|
|
||||||
// Dummy writes / reads
|
// Dummy writes / reads
|
||||||
volatile u32 data;
|
if (s->boot_path == BOOT_PATH_NORMAL) {
|
||||||
FOR_EACH_POPULATED_RANK(s->dimms, ch, r) {
|
volatile u32 data;
|
||||||
for (bank = 0; bank < 4; bank++) {
|
FOR_EACH_POPULATED_RANK(s->dimms, ch, r) {
|
||||||
reg32 = (ch << 29) | (r*0x8000000) | (bank << 12);
|
for (bank = 0; bank < 4; bank++) {
|
||||||
write32((u32 *)reg32, 0xffffffff);
|
reg32 = (ch << 29) | (r*0x8000000) |
|
||||||
data = read32((u32 *)reg32);
|
(bank << 12);
|
||||||
printk(BIOS_DEBUG, "Wrote ones, Read: [0x%08x]=0x%08x\n", reg32, data);
|
write32((u32 *)reg32, 0xffffffff);
|
||||||
write32((u32 *)reg32, 0x00000000);
|
data = read32((u32 *)reg32);
|
||||||
data = read32((u32 *)reg32);
|
printk(BIOS_DEBUG, "Wrote ones,");
|
||||||
printk(BIOS_DEBUG, "Wrote zeros, Read: [0x%08x]=0x%08x\n", reg32, data);
|
printk(BIOS_DEBUG, " Read: [0x%08x]=0x%08x\n",
|
||||||
|
reg32, data);
|
||||||
|
write32((u32 *)reg32, 0x00000000);
|
||||||
|
data = read32((u32 *)reg32);
|
||||||
|
printk(BIOS_DEBUG, "Wrote zeros,");
|
||||||
|
printk(BIOS_DEBUG, " Read: [0x%08x]=0x%08x\n",
|
||||||
|
reg32, data);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
printk(BIOS_DEBUG, "Done dummy reads\n");
|
printk(BIOS_DEBUG, "Done dummy reads\n");
|
||||||
|
|
|
@ -87,8 +87,8 @@
|
||||||
#define MCHBAR32(x) *((volatile u32 *)(DEFAULT_MCHBAR + x))
|
#define MCHBAR32(x) *((volatile u32 *)(DEFAULT_MCHBAR + x))
|
||||||
|
|
||||||
#define PMSTS_MCHBAR 0x0f14 /* Self refresh channel status */
|
#define PMSTS_MCHBAR 0x0f14 /* Self refresh channel status */
|
||||||
#define PMSTS_WARM_RESET (1 << 1)
|
#define PMSTS_WARM_RESET (1 << 8)
|
||||||
#define PMSTS_BOTH_SELFREFRESH (1 << 0)
|
#define PMSTS_BOTH_SELFREFRESH (3 << 0)
|
||||||
|
|
||||||
#define CLKCFG_MCHBAR 0x0c00
|
#define CLKCFG_MCHBAR 0x0c00
|
||||||
#define CLKCFG_FSBCLK_SHIFT 0
|
#define CLKCFG_FSBCLK_SHIFT 0
|
||||||
|
@ -290,6 +290,9 @@ struct sysinfo {
|
||||||
struct dimminfo dimms[4];
|
struct dimminfo dimms[4];
|
||||||
u8 spd_map[4];
|
u8 spd_map[4];
|
||||||
};
|
};
|
||||||
|
#define BOOT_PATH_NORMAL 0
|
||||||
|
#define BOOT_PATH_WARM_RESET 1
|
||||||
|
#define BOOT_PATH_RESUME 2
|
||||||
|
|
||||||
enum ddr2_signals {
|
enum ddr2_signals {
|
||||||
CLKSET0 = 0,
|
CLKSET0 = 0,
|
||||||
|
|
Loading…
Reference in New Issue