Intel e7505: enable ECC scrubbing

It takes about 3 seconds to scrub 8GiB DDR266 RAM.

After ECC scrub XIP cache is disabled for system stability. There is
very little to do in romstage after ECC scrub, especially when RAM
debug messages are turned off. So the delay caused by this is hardly
noticeable.

Cache for complete ROM is re-enabled before ramstage is decompressed,
and it has no unstability issues. So the code required to re-enable
cache for ROM currently already exists in cache-as-ram_ht.inc.

A Kconfig option HW_SCRUBBER enables the scrub to be run on hard
reboots and power-ons.

Change-Id: Icf27acf73240c06b58091f1229efc0f01cca3f85
Signed-off-by: Kyösti Mälkki <kyosti.malkki@gmail.com>
Reviewed-on: http://review.coreboot.org/905
Tested-by: build bot (Jenkins)
Reviewed-by: Patrick Georgi <patrick@georgi-clan.de>
This commit is contained in:
Kyösti Mälkki 2012-04-18 20:33:35 +03:00 committed by Patrick Georgi
parent a8111cf980
commit 97c064f034
5 changed files with 160 additions and 41 deletions

View File

@ -13,6 +13,7 @@ config BOARD_SPECIFIC_OPTIONS # dummy
select UDELAY_TSC select UDELAY_TSC
select HAVE_ACPI_TABLES select HAVE_ACPI_TABLES
select BOARD_ROMSIZE_KB_512 select BOARD_ROMSIZE_KB_512
select HW_SCRUBBER
config MAINBOARD_DIR config MAINBOARD_DIR
string string

View File

@ -68,8 +68,23 @@ void main(unsigned long bist)
// If this is a warm boot, some initialization can be skipped // If this is a warm boot, some initialization can be skipped
if (!bios_reset_detected()) { if (!bios_reset_detected()) {
enable_smbus(); enable_smbus();
sdram_initialize(ARRAY_SIZE(memctrl), memctrl);
/* The real MCH initialisation. */
e7505_mch_init(memctrl);
/*
* ECC scrub invalidates cache, so all stack in CAR
* is lost. Only return addresses from main() and
* scrub_ecc() are recovered to stack via xmm0-xmm3.
*/
#if CONFIG_HW_SCRUBBER
unsigned long ret_addr = (unsigned long)((unsigned long*)&bist - 1);
e7505_mch_scrub_ecc(ret_addr);
#endif
/* Hook for post ECC scrub settings and debug. */
e7505_mch_done(memctrl);
} }
print_debug("SDRAM is up.\n"); printk(BIOS_DEBUG, "SDRAM is up.\n");
} }

View File

@ -1,4 +1,33 @@
##
## This file is part of the coreboot project.
##
## Copyright (C) 2007-2012 coresystems GmbH
##
## 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; version 2 of the License.
##
## 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.
##
## You should have received a copy of the GNU General Public License
## along with this program; if not, write to the Free Software
## Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
##
config NORTHBRIDGE_INTEL_E7505 config NORTHBRIDGE_INTEL_E7505
bool bool
if NORTHBRIDGE_INTEL_E7505
config NORTHBRIDGE_SPECIFIC_OPTIONS # dummy
def_bool y
select HAVE_DEBUG_RAM_SETUP select HAVE_DEBUG_RAM_SETUP
config HW_SCRUBBER
bool
default n
endif

View File

@ -11,6 +11,9 @@
/* converted to C 6/2004 yhlu */ /* converted to C 6/2004 yhlu */
#include <cpu/x86/mtrr.h>
#include <cpu/x86/cache.h>
#include <cpu/x86/msr.h>
#include <assert.h> #include <assert.h>
#include <spd.h> #include <spd.h>
#include <sdram_mode.h> #include <sdram_mode.h>
@ -918,44 +921,93 @@ static void configure_e7501_ram_addresses(const struct mem_controller
} }
/** /**
* If we're configured to use ECC, initialize the SDRAM and clear the E7501's * Execute ECC full-speed scrub once and leave scrubber disabled.
* ECC error flags. *
* NOTE: All cache and stack is lost during ECC scrub loop.
*/ */
#ifdef __ROMCC__ static void __attribute__((always_inline))
static void initialize_ecc(void) initialize_ecc(unsigned long ret_addr, unsigned long ret_addr2)
{ {
uint32_t dram_controller_mode; uint16_t scrubbed = pci_read_config16(MCHDEV, MCHCFGNS) & 0x08;
/* Test to see if ECC support is enabled */
dram_controller_mode = pci_read_config32(MCHDEV, DRC);
dram_controller_mode >>= 20;
dram_controller_mode &= 3;
if (dram_controller_mode == 2) {
uint8_t byte;
if (!scrubbed) {
RAM_DEBUG_MESSAGE("Initializing ECC state...\n"); RAM_DEBUG_MESSAGE("Initializing ECC state...\n");
pci_write_config8(MCHDEV, MCHCFGNS, 0x01);
// Wait for scrub cycle to complete /* ECC scrub flushes cache-lines and stack, need to
* store return address from romstage.c:main().
*/
asm volatile(
"movd %0, %%xmm0;"
"movd (%0), %%xmm1;"
"movd %1, %%xmm2;"
"movd (%1), %%xmm3;"
:: "r" (ret_addr), "r" (ret_addr2) :
);
/* NOTE: All cache is lost during this loop.
* Make sure PCI access does not use stack.
*/
pci_write_config16(MCHDEV, MCHCFGNS, 0x01);
do { do {
byte = scrubbed = pci_read_config16(MCHDEV, MCHCFGNS);
pci_read_config8(MCHDEV, MCHCFGNS); } while (! (scrubbed & 0x08));
} while ((byte & 0x08) == 0); pci_write_config16(MCHDEV, MCHCFGNS, (scrubbed & ~0x07) | 0x04);
/* Some problem remains with XIP cache from ROM, so for
* now, I disable XIP and also invalidate cache (again)
* before the remaining small portion of romstage.
*
* Adding NOPs here has unexpected results, making
* the first do_printk()/vtxprintf() after ECC scrub
* fail midway. Sometimes vtxprintf() dumps strings
* completely but with every 4th (fourth) character as "/".
*
* An inlined dump to console of the same string,
* before vtxprintf() call, is successful. So the
* source string should be completely in cache already.
*
* I need to review this again with CPU microcode
* update applied pre-CAR.
*/
/* Disable and invalidate all cache. */
msr_t xip_mtrr = rdmsr(MTRRphysMask_MSR(1));
xip_mtrr.lo &= ~MTRRphysMaskValid;
invd();
wrmsr(MTRRphysMask_MSR(1), xip_mtrr);
invd();
pci_write_config8(MCHDEV, MCHCFGNS, (byte & 0xfc) | 0x04);
RAM_DEBUG_MESSAGE("ECC state initialized.\n"); RAM_DEBUG_MESSAGE("ECC state initialized.\n");
/* Clear the ECC error bits */ /* Recover IP for return from main. */
pci_write_config8(RASDEV, DRAM_FERR, 0x03); asm volatile(
pci_write_config8(RASDEV, DRAM_NERR, 0x03); "movd %%xmm0, %%edi;"
"movd %%xmm1, (%%edi);"
"movd %%xmm2, %%edi;"
"movd %%xmm3, (%%edi);"
::: "edi"
);
// Clear DRAM Interface error bits (write-one-clear) #if CONFIG_DEBUG_RAM_SETUP
pci_write_config32(RASDEV, FERR_GLOBAL, 1 << 18); unsigned int a1, a2;
pci_write_config32(RASDEV, NERR_GLOBAL, 1 << 18); asm volatile("movd %%xmm2, %%eax;" : "=a" (a1) ::);
asm volatile("movd %%xmm3, %%eax;" : "=a" (a2) ::);
printk(BIOS_DEBUG, "return EIP @ %x = %x\n", a1, a2);
asm volatile("movd %%xmm0, %%eax;" : "=a" (a1) ::);
asm volatile("movd %%xmm1, %%eax;" : "=a" (a2) ::);
printk(BIOS_DEBUG, "return EIP @ %x = %x\n", a1, a2);
#endif
} }
/* Clear the ECC error bits. */
pci_write_config8(RASDEV, DRAM_FERR, 0x03);
pci_write_config8(RASDEV, DRAM_NERR, 0x03);
/* Clear DRAM Interface error bits. */
pci_write_config32(RASDEV, FERR_GLOBAL, 1 << 18);
pci_write_config32(RASDEV, NERR_GLOBAL, 1 << 18);
} }
#endif
/** /**
* Program the DRAM Timing register (DRT) of the E7501 (except for CAS# * Program the DRAM Timing register (DRT) of the E7501 (except for CAS#
@ -1669,18 +1721,19 @@ static void sdram_enable(const struct mem_controller *ctrl)
dram_controller_mode |= (1 << 29); dram_controller_mode |= (1 << 29);
pci_write_config32(MCHDEV, DRC, dram_controller_mode); pci_write_config32(MCHDEV, DRC, dram_controller_mode);
EXTRA_DELAY; EXTRA_DELAY;
}
#ifdef __ROMCC__ /**
/* Problems with cache-as-ram, disable for now */ * @param ctrl PCI addresses of memory controller functions, and SMBus
initialize_ecc(); * addresses of DIMM slots on the mainboard.
#endif */
static void sdram_post_ecc(const struct mem_controller *ctrl)
dram_controller_mode = pci_read_config32(MCHDEV, DRC); /* FCS_EN */ {
dram_controller_mode |= (1 << 17); // NOTE: undocumented reserved bit /* Fast CS# Enable. */
uint32_t dram_controller_mode = pci_read_config32(MCHDEV, DRC);
dram_controller_mode = pci_read_config32(MCHDEV, DRC);
dram_controller_mode |= (1 << 17);
pci_write_config32(MCHDEV, DRC, dram_controller_mode); pci_write_config32(MCHDEV, DRC, dram_controller_mode);
RAM_DEBUG_MESSAGE("Northbridge following SDRAM init:\n");
DUMPNORTH();
} }
/** /**
@ -1815,7 +1868,7 @@ static void sdram_set_registers(const struct mem_controller *ctrl)
* *
* *
*/ */
void sdram_initialize(int controllers, const struct mem_controller *memctrl) void e7505_mch_init(const struct mem_controller *memctrl)
{ {
RAM_DEBUG_MESSAGE("Northbridge prior to SDRAM init:\n"); RAM_DEBUG_MESSAGE("Northbridge prior to SDRAM init:\n");
DUMPNORTH(); DUMPNORTH();
@ -1825,6 +1878,27 @@ void sdram_initialize(int controllers, const struct mem_controller *memctrl)
sdram_enable(memctrl); sdram_enable(memctrl);
} }
/**
* Scrub and reset error counts for ECC dimms.
*
* NOTE: this will invalidate cache and disable XIP cache for the
* short remaining part of romstage.
*/
void e7505_mch_scrub_ecc(unsigned long ret_addr)
{
unsigned long ret_addr2 = (unsigned long)((unsigned long*)&ret_addr-1);
if ((pci_read_config32(MCHDEV, DRC)>>20 & 3) == 2)
initialize_ecc(ret_addr, ret_addr2);
}
void e7505_mch_done(const struct mem_controller *memctrl)
{
sdram_post_ecc(memctrl);
RAM_DEBUG_MESSAGE("Northbridge following SDRAM init:\n");
DUMPNORTH();
}
static int bios_reset_detected(void) static int bios_reset_detected(void)
{ {
uint32_t dword = pci_read_config32(MCHDEV, DRC); uint32_t dword = pci_read_config32(MCHDEV, DRC);

View File

@ -15,8 +15,8 @@ struct mem_controller {
uint16_t channel1[MAX_DIMM_SOCKETS_PER_CHANNEL]; uint16_t channel1[MAX_DIMM_SOCKETS_PER_CHANNEL];
}; };
#ifndef __ROMCC__ void e7505_mch_init(const struct mem_controller *memctrl);
void sdram_initialize(int controllers, const struct mem_controller *ctrl); void e7505_mch_scrub_ecc(unsigned long ret_addr);
#endif void e7505_mch_done(const struct mem_controller *memctrl);
#endif /* RAMINIT_H */ #endif /* RAMINIT_H */