ppc4xx: Refactor ECC POST for AMCC Denali core

The ECC POST reported intermittent failures running after power-up on
the Korat PPC440EPx board.  Even when the test passed, the debugging
output occasionally reported additional unexpected ECC errors.

This refactoring has three main objectives: (1) minimize the code
executed with ECC enabled during the tests, (2) add more checking of the
results so any unexpected ECC errors would cause the test to fail, and
(3) use synchronization (only) where required by the processor.

Signed-off-by: Larry Johnson <lrj@acm.org>
master
Larry Johnson 17 years ago committed by Stefan Roese
parent 8d99cd0691
commit 4b3cc6ece9
  1. 266
      post/cpu/ppc4xx/denali_ecc.c

@ -49,7 +49,7 @@
DECLARE_GLOBAL_DATA_PTR; DECLARE_GLOBAL_DATA_PTR;
const static unsigned char syndrome_codes[] = { const static uint8_t syndrome_codes[] = {
0xF4, 0XF1, 0XEC, 0XEA, 0XE9, 0XE6, 0XE5, 0XE3, 0xF4, 0XF1, 0XEC, 0XEA, 0XE9, 0XE6, 0XE5, 0XE3,
0XDC, 0XDA, 0XD9, 0XD6, 0XD5, 0XD3, 0XCE, 0XCB, 0XDC, 0XDA, 0XD9, 0XD6, 0XD5, 0XD3, 0XCE, 0XCB,
0xB5, 0XB0, 0XAD, 0XAB, 0XA8, 0XA7, 0XA4, 0XA2, 0xB5, 0XB0, 0XAD, 0XAB, 0XA8, 0XA7, 0XA4, 0XA2,
@ -65,174 +65,183 @@ const static unsigned char syndrome_codes[] = {
#define ECC_STOP_ADDR 0x2000 #define ECC_STOP_ADDR 0x2000
#define ECC_PATTERN 0x01010101 #define ECC_PATTERN 0x01010101
#define ECC_PATTERN_CORR 0x11010101 #define ECC_PATTERN_CORR 0x11010101
#define ECC_PATTERN_UNCORR 0xF1010101 #define ECC_PATTERN_UNCORR 0x61010101
static int test_ecc_error(void) inline static void disable_ecc(void)
{ {
unsigned long value; uint32_t value;
unsigned long hdata, ldata, haddr, laddr;
unsigned int bit;
int ret = 0; sync(); /* Wait for any pending memory accesses to complete. */
mfsdram(DDR0_22, value);
mfsdram(DDR0_23, value); mtsdram(DDR0_22, (value & ~DDR0_22_CTRL_RAW_MASK)
| DDR0_22_CTRL_RAW_ECC_DISABLE);
}
for (bit = 0; bit < sizeof(syndrome_codes); bit++) inline static void clear_and_enable_ecc(void)
if (syndrome_codes[bit] == ((value >> 16) & 0xff)) {
break; uint32_t value;
sync(); /* Wait for any pending memory accesses to complete. */
mfsdram(DDR0_00, value); mfsdram(DDR0_00, value);
mtsdram(DDR0_00, value | DDR0_00_INT_ACK_ALL);
mfsdram(DDR0_22, value);
mtsdram(DDR0_22, (value & ~DDR0_22_CTRL_RAW_MASK)
| DDR0_22_CTRL_RAW_ECC_ENABLE);
}
static uint32_t get_ecc_status(void)
{
uint32_t int_status;
#if defined(DEBUG)
uint8_t syndrome;
uint32_t hdata, ldata, haddr, laddr;
uint32_t value;
#endif
mfsdram(DDR0_00, int_status);
int_status &= DDR0_00_INT_STATUS_MASK;
if (value & DDR0_00_INT_STATUS_BIT0) { #if defined(DEBUG)
debug("Bit0. A single access outside the defined PHYSICAL" if (int_status & (DDR0_00_INT_STATUS_BIT0 | DDR0_00_INT_STATUS_BIT1)) {
" memory space detected\n");
mfsdram(DDR0_32, laddr); mfsdram(DDR0_32, laddr);
mfsdram(DDR0_33, haddr); mfsdram(DDR0_33, haddr);
debug(" addr = 0x%08x%08x\n", haddr, laddr); haddr &= 0x00000001;
ret = 1; if (int_status & DDR0_00_INT_STATUS_BIT1)
} debug("Multiple accesses");
if (value & DDR0_00_INT_STATUS_BIT1) { else
debug("Bit1. Multiple accesses outside the defined PHYSICAL" debug("A single access");
" memory space detected\n");
ret = 2; debug(" outside the defined physical memory space detected\n"
} " addr = 0x%01x%08x\n", haddr, laddr);
if (value & DDR0_00_INT_STATUS_BIT2) {
debug("Bit2. Single correctable ECC event detected\n");
mfsdram(DDR0_38, laddr);
mfsdram(DDR0_39, haddr);
mfsdram(DDR0_40, ldata);
mfsdram(DDR0_41, hdata);
debug(" 0x%08x - 0x%08x%08x, bit - %d\n",
laddr, hdata, ldata, bit);
ret = 3;
} }
if (value & DDR0_00_INT_STATUS_BIT3) { if (int_status & (DDR0_00_INT_STATUS_BIT2 | DDR0_00_INT_STATUS_BIT3)) {
debug("Bit3. Multiple correctable ECC events detected\n"); unsigned int bit;
mfsdram(DDR0_23, value);
syndrome = (value >> 16) & 0xff;
for (bit = 0; bit < sizeof(syndrome_codes); bit++)
if (syndrome_codes[bit] == syndrome)
break;
mfsdram(DDR0_38, laddr); mfsdram(DDR0_38, laddr);
mfsdram(DDR0_39, haddr); mfsdram(DDR0_39, haddr);
haddr &= 0x00000001;
mfsdram(DDR0_40, ldata); mfsdram(DDR0_40, ldata);
mfsdram(DDR0_41, hdata); mfsdram(DDR0_41, hdata);
debug(" 0x%08x - 0x%08x%08x, bit - %d\n", if (int_status & DDR0_00_INT_STATUS_BIT3)
laddr, hdata, ldata, bit); debug("Multiple correctable ECC events");
ret = 4; else
} debug("Single correctable ECC event");
if (value & DDR0_00_INT_STATUS_BIT4) {
debug("Bit4. Single uncorrectable ECC event detected\n"); debug(" detected\n 0x%01x%08x - 0x%08x%08x, bit - %d\n",
mfsdram(DDR0_34, laddr); haddr, laddr, hdata, ldata, bit);
mfsdram(DDR0_35, haddr);
mfsdram(DDR0_36, ldata);
mfsdram(DDR0_37, hdata);
debug(" 0x%08x - 0x%08x%08x, bit - %d\n",
laddr, hdata, ldata, bit);
ret = 5;
} }
if (value & DDR0_00_INT_STATUS_BIT5) { if (int_status & (DDR0_00_INT_STATUS_BIT4 | DDR0_00_INT_STATUS_BIT5)) {
debug("Bit5. Multiple uncorrectable ECC events detected\n"); mfsdram(DDR0_23, value);
syndrome = (value >> 8) & 0xff;
mfsdram(DDR0_34, laddr); mfsdram(DDR0_34, laddr);
mfsdram(DDR0_35, haddr); mfsdram(DDR0_35, haddr);
haddr &= 0x00000001;
mfsdram(DDR0_36, ldata); mfsdram(DDR0_36, ldata);
mfsdram(DDR0_37, hdata); mfsdram(DDR0_37, hdata);
debug(" 0x%08x - 0x%08x%08x, bit - %d\n", if (int_status & DDR0_00_INT_STATUS_BIT5)
laddr, hdata, ldata, bit); debug("Multiple uncorrectable ECC events");
ret = 6; else
} debug("Single uncorrectable ECC event");
if (value & DDR0_00_INT_STATUS_BIT6) {
debug("Bit6. DRAM initialization complete\n"); debug(" detected\n 0x%01x%08x - 0x%08x%08x, "
ret = 7; "syndrome - 0x%02x\n",
haddr, laddr, hdata, ldata, syndrome);
} }
if (int_status & DDR0_00_INT_STATUS_BIT6)
debug("DRAM initialization complete\n");
#endif /* defined(DEBUG) */
/* error status cleared */ return int_status;
mfsdram(DDR0_00, value);
mtsdram(DDR0_00, value | DDR0_00_INT_ACK_ALL);
return ret;
} }
static int test_ecc(unsigned long ecc_addr) static int test_ecc(uint32_t ecc_addr)
{ {
unsigned long value; uint32_t value;
volatile unsigned *const ecc_mem = (volatile unsigned *) ecc_addr; volatile uint32_t *const ecc_mem = (volatile uint32_t *)ecc_addr;
int pret;
int ret = 0; int ret = 0;
sync();
eieio();
WATCHDOG_RESET(); WATCHDOG_RESET();
debug("Entering test_ecc(0x%08lX)\n", ecc_addr); debug("Entering test_ecc(0x%08x)\n", ecc_addr);
/* Set up correct ECC in memory */
disable_ecc();
clear_and_enable_ecc();
out_be32(ecc_mem, ECC_PATTERN); out_be32(ecc_mem, ECC_PATTERN);
out_be32(ecc_mem + 1, ECC_PATTERN); out_be32(ecc_mem + 1, ECC_PATTERN);
in_be32(ecc_mem);
pret = test_ecc_error(); /* Verify no ECC error reading back */
if (pret != 0) { value = in_be32(ecc_mem);
debug("pret: expected 0, got %d\n", pret); disable_ecc();
if (ECC_PATTERN != value) {
debug("Data read error (no-error case): "
"expected 0x%08x, read 0x%08x\n", ECC_PATTERN, value);
ret = 1;
}
value = get_ecc_status();
if (0x00000000 != value) {
/* Expected no ECC status reported */
debug("get_ecc_status(): expected 0x%08x, got 0x%08x\n",
0x00000000, value);
ret = 1; ret = 1;
} }
/* test for correctable error */
/* disconnect from ecc storage */
mfsdram(DDR0_22, value);
mtsdram(DDR0_22, (value & ~DDR0_22_CTRL_RAW_MASK)
| DDR0_22_CTRL_RAW_ECC_DISABLE);
/* creating (correctable) single-bit error */ /* Test for correctable error by creating a one-bit error */
out_be32(ecc_mem, ECC_PATTERN_CORR); out_be32(ecc_mem, ECC_PATTERN_CORR);
clear_and_enable_ecc();
/* enable ecc */ value = in_be32(ecc_mem);
mfsdram(DDR0_22, value); disable_ecc();
mtsdram(DDR0_22, (value & ~DDR0_22_CTRL_RAW_MASK) /* Test that the corrected data was read */
| DDR0_22_CTRL_RAW_ECC_ENABLE); if (ECC_PATTERN != value) {
sync(); debug("Data read error (correctable-error case): "
eieio(); "expected 0x%08x, read 0x%08x\n", ECC_PATTERN, value);
ret = 1;
in_be32(ecc_mem); }
pret = test_ecc_error(); value = get_ecc_status();
/* if read data ok, 1 correctable error must be fixed */ if ((DDR0_00_INT_STATUS_BIT2 | DDR0_00_INT_STATUS_BIT7) != value) {
if (pret != 3) { /* Expected a single correctable error reported */
debug("pret: expected 3, got %d\n", pret); debug("get_ecc_status(): expected 0x%08x, got 0x%08x\n",
DDR0_00_INT_STATUS_BIT2, value);
ret = 1; ret = 1;
} }
/* test for uncorrectable error */
/* disconnect from ecc storage */
mfsdram(DDR0_22, value);
mtsdram(DDR0_22, (value & ~DDR0_22_CTRL_RAW_MASK)
| DDR0_22_CTRL_RAW_NO_ECC_RAM);
/* creating (uncorrectable) multiple-bit error */ /* Test for uncorrectable error by creating a two-bit error */
out_be32(ecc_mem, ECC_PATTERN_UNCORR); out_be32(ecc_mem, ECC_PATTERN_UNCORR);
clear_and_enable_ecc();
/* enable ecc */ value = in_be32(ecc_mem);
mfsdram(DDR0_22, value); disable_ecc();
mtsdram(DDR0_22, (value & ~DDR0_22_CTRL_RAW_MASK) /* Test that the corrected data was read */
| DDR0_22_CTRL_RAW_ECC_ENABLE); if (ECC_PATTERN_UNCORR != value) {
sync(); debug("Data read error (uncorrectable-error case): "
eieio(); "expected 0x%08x, read 0x%08x\n", ECC_PATTERN_UNCORR,
value);
in_be32(ecc_mem); ret = 1;
pret = test_ecc_error(); }
/* info about uncorrectable error must appear */ value = get_ecc_status();
if (pret != 5) { if ((DDR0_00_INT_STATUS_BIT4 | DDR0_00_INT_STATUS_BIT7) != value) {
debug("pret: expected 5, got %d\n", pret); /* Expected a single uncorrectable error reported */
debug("get_ecc_status(): expected 0x%08x, got 0x%08x\n",
DDR0_00_INT_STATUS_BIT4, value);
ret = 1; ret = 1;
} }
/* remove error from SDRAM */
/* Remove error from SDRAM and enable ECC. */
out_be32(ecc_mem, ECC_PATTERN); out_be32(ecc_mem, ECC_PATTERN);
/* clear error caused by read-modify-write */ clear_and_enable_ecc();
mfsdram(DDR0_00, value);
mtsdram(DDR0_00, value | DDR0_00_INT_ACK_ALL);
sync();
eieio();
return ret; return ret;
} }
int ecc_post_test (int flags) int ecc_post_test(int flags)
{ {
int ret = 0; int ret = 0;
unsigned long value; uint32_t value;
unsigned long iaddr; uint32_t iaddr;
sync();
eieio();
mfsdram(DDR0_22, value); mfsdram(DDR0_22, value);
if (0x3 != DDR0_22_CTRL_RAW_DECODE(value)) { if (0x3 != DDR0_22_CTRL_RAW_DECODE(value)) {
@ -240,28 +249,23 @@ int ecc_post_test (int flags)
return 0; return 0;
} }
/* mask all int */ /* Mask all interrupts. */
mfsdram(DDR0_01, value); mfsdram(DDR0_01, value);
mtsdram(DDR0_01, (value & ~DDR0_01_INT_MASK_MASK) mtsdram(DDR0_01, (value & ~DDR0_01_INT_MASK_MASK)
| DDR0_01_INT_MASK_ALL_OFF); | DDR0_01_INT_MASK_ALL_OFF);
/* clear error status */
mfsdram(DDR0_00, value);
mtsdram(DDR0_00, value | DDR0_00_INT_ACK_ALL);
for (iaddr = ECC_START_ADDR; iaddr <= ECC_STOP_ADDR; iaddr += iaddr) { for (iaddr = ECC_START_ADDR; iaddr <= ECC_STOP_ADDR; iaddr += iaddr) {
ret = test_ecc(iaddr); ret = test_ecc(iaddr);
if (ret) if (ret)
break; break;
} }
/* /*
* Clear possible errors resulting from ECC testing. * Clear possible errors resulting from ECC testing. (If not done, we
* If not done, then we could get an interrupt later on when * we could get an interrupt later on when exceptions are enabled.)
* exceptions are enabled.
*/ */
set_mcsr(get_mcsr()); set_mcsr(get_mcsr());
debug("ecc_post_test() returning %d\n", ret);
return ret; return ret;
} }
#endif /* CONFIG_POST & CFG_POST_ECC */ #endif /* CONFIG_POST & CFG_POST_ECC */
#endif /* defined(CONFIG_POST) && ... */ #endif /* defined(CONFIG_POST) && ... */

Loading…
Cancel
Save