|  | /* | 
|  | * (C) Copyright 2007 | 
|  | * Developed for DENX Software Engineering GmbH. | 
|  | * | 
|  | * Author: Pavel Kolesnikov <concord@emcraft.com> | 
|  | * | 
|  | * See file CREDITS for list of people who contributed to this | 
|  | * project. | 
|  | * | 
|  | * 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. | 
|  | * | 
|  | * 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., 59 Temple Place, Suite 330, Boston, | 
|  | * MA 02111-1307 USA | 
|  | */ | 
|  |  | 
|  | /* define DEBUG for debugging output (obviously ;-)) */ | 
|  | #if 0 | 
|  | #define DEBUG | 
|  | #endif | 
|  |  | 
|  | #include <common.h> | 
|  | #include <watchdog.h> | 
|  |  | 
|  | #if defined(CONFIG_440EPX) || defined(CONFIG_440GRX) | 
|  |  | 
|  | #include <post.h> | 
|  |  | 
|  | #if CONFIG_POST & CONFIG_SYS_POST_ECC | 
|  |  | 
|  | /* | 
|  | * MEMORY ECC test | 
|  | * | 
|  | * This test performs the checks ECC facility of memory. | 
|  | */ | 
|  | #include <asm/processor.h> | 
|  | #include <asm/mmu.h> | 
|  | #include <asm/io.h> | 
|  | #include <ppc440.h> | 
|  |  | 
|  | DECLARE_GLOBAL_DATA_PTR; | 
|  |  | 
|  | const static uint8_t syndrome_codes[] = { | 
|  | 0xF4, 0XF1, 0XEC, 0XEA, 0XE9, 0XE6, 0XE5, 0XE3, | 
|  | 0XDC, 0XDA, 0XD9, 0XD6, 0XD5, 0XD3, 0XCE, 0XCB, | 
|  | 0xB5, 0XB0, 0XAD, 0XAB, 0XA8, 0XA7, 0XA4, 0XA2, | 
|  | 0X9D, 0X9B, 0X98, 0X97, 0X94, 0X92, 0X8F, 0X8A, | 
|  | 0x75, 0x70, 0X6D, 0X6B, 0X68, 0X67, 0X64, 0X62, | 
|  | 0X5E, 0X5B, 0X58, 0X57, 0X54, 0X52, 0X4F, 0X4A, | 
|  | 0x34, 0x31, 0X2C, 0X2A, 0X29, 0X26, 0X25, 0X23, | 
|  | 0X1C, 0X1A, 0X19, 0X16, 0X15, 0X13, 0X0E, 0X0B, | 
|  | 0x80, 0x40, 0x20, 0x10, 0x08, 0x04, 0x02, 0x01 | 
|  | }; | 
|  |  | 
|  | #define ECC_START_ADDR		0x10 | 
|  | #define ECC_STOP_ADDR		0x2000 | 
|  | #define ECC_PATTERN		0x01010101 | 
|  | #define ECC_PATTERN_CORR	0x11010101 | 
|  | #define ECC_PATTERN_UNCORR	0x61010101 | 
|  |  | 
|  | inline static void disable_ecc(void) | 
|  | { | 
|  | uint32_t value; | 
|  |  | 
|  | sync(); /* Wait for any pending memory accesses to complete. */ | 
|  | mfsdram(DDR0_22, value); | 
|  | mtsdram(DDR0_22, (value & ~DDR0_22_CTRL_RAW_MASK) | 
|  | | DDR0_22_CTRL_RAW_ECC_DISABLE); | 
|  | } | 
|  |  | 
|  | inline static void clear_and_enable_ecc(void) | 
|  | { | 
|  | uint32_t value; | 
|  |  | 
|  | sync(); /* Wait for any pending memory accesses to complete. */ | 
|  | 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 defined(DEBUG) | 
|  | if (int_status & (DDR0_00_INT_STATUS_BIT0 | DDR0_00_INT_STATUS_BIT1)) { | 
|  | mfsdram(DDR0_32, laddr); | 
|  | mfsdram(DDR0_33, haddr); | 
|  | haddr &= 0x00000001; | 
|  | if (int_status & DDR0_00_INT_STATUS_BIT1) | 
|  | debug("Multiple accesses"); | 
|  | else | 
|  | debug("A single access"); | 
|  |  | 
|  | debug(" outside the defined physical memory space detected\n" | 
|  | "        addr = 0x%01x%08x\n", haddr, laddr); | 
|  | } | 
|  | if (int_status & (DDR0_00_INT_STATUS_BIT2 | DDR0_00_INT_STATUS_BIT3)) { | 
|  | 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_39, haddr); | 
|  | haddr &= 0x00000001; | 
|  | mfsdram(DDR0_40, ldata); | 
|  | mfsdram(DDR0_41, hdata); | 
|  | if (int_status & DDR0_00_INT_STATUS_BIT3) | 
|  | debug("Multiple correctable ECC events"); | 
|  | else | 
|  | debug("Single correctable ECC event"); | 
|  |  | 
|  | debug(" detected\n        0x%01x%08x - 0x%08x%08x, bit - %d\n", | 
|  | haddr, laddr, hdata, ldata, bit); | 
|  | } | 
|  | if (int_status & (DDR0_00_INT_STATUS_BIT4 | DDR0_00_INT_STATUS_BIT5)) { | 
|  | mfsdram(DDR0_23, value); | 
|  | syndrome = (value >> 8) & 0xff; | 
|  | mfsdram(DDR0_34, laddr); | 
|  | mfsdram(DDR0_35, haddr); | 
|  | haddr &= 0x00000001; | 
|  | mfsdram(DDR0_36, ldata); | 
|  | mfsdram(DDR0_37, hdata); | 
|  | if (int_status & DDR0_00_INT_STATUS_BIT5) | 
|  | debug("Multiple uncorrectable ECC events"); | 
|  | else | 
|  | debug("Single uncorrectable ECC event"); | 
|  |  | 
|  | debug(" detected\n        0x%01x%08x - 0x%08x%08x, " | 
|  | "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) */ | 
|  |  | 
|  | return int_status; | 
|  | } | 
|  |  | 
|  | static int test_ecc(uint32_t ecc_addr) | 
|  | { | 
|  | uint32_t value; | 
|  | volatile uint32_t *const ecc_mem = (volatile uint32_t *)ecc_addr; | 
|  | int ret = 0; | 
|  |  | 
|  | WATCHDOG_RESET(); | 
|  |  | 
|  | 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 + 1, ECC_PATTERN); | 
|  |  | 
|  | /* Verify no ECC error reading back */ | 
|  | value = in_be32(ecc_mem); | 
|  | 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; | 
|  | } | 
|  |  | 
|  | /* Test for correctable error by creating a one-bit error */ | 
|  | out_be32(ecc_mem, ECC_PATTERN_CORR); | 
|  | clear_and_enable_ecc(); | 
|  | value = in_be32(ecc_mem); | 
|  | disable_ecc(); | 
|  | /* Test that the corrected data was read */ | 
|  | if (ECC_PATTERN != value) { | 
|  | debug("Data read error (correctable-error case): " | 
|  | "expected 0x%08x, read 0x%08x\n", ECC_PATTERN, value); | 
|  | ret = 1; | 
|  | } | 
|  | value = get_ecc_status(); | 
|  | if ((DDR0_00_INT_STATUS_BIT2 | DDR0_00_INT_STATUS_BIT7) != value) { | 
|  | /* Expected a single correctable error reported */ | 
|  | debug("get_ecc_status(): expected 0x%08x, got 0x%08x\n", | 
|  | DDR0_00_INT_STATUS_BIT2, value); | 
|  | ret = 1; | 
|  | } | 
|  |  | 
|  | /* Test for uncorrectable error by creating a two-bit error */ | 
|  | out_be32(ecc_mem, ECC_PATTERN_UNCORR); | 
|  | clear_and_enable_ecc(); | 
|  | value = in_be32(ecc_mem); | 
|  | disable_ecc(); | 
|  | /* Test that the corrected data was read */ | 
|  | if (ECC_PATTERN_UNCORR != value) { | 
|  | debug("Data read error (uncorrectable-error case): " | 
|  | "expected 0x%08x, read 0x%08x\n", ECC_PATTERN_UNCORR, | 
|  | value); | 
|  | ret = 1; | 
|  | } | 
|  | value = get_ecc_status(); | 
|  | if ((DDR0_00_INT_STATUS_BIT4 | DDR0_00_INT_STATUS_BIT7) != value) { | 
|  | /* 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; | 
|  | } | 
|  |  | 
|  | /* Remove error from SDRAM and enable ECC. */ | 
|  | out_be32(ecc_mem, ECC_PATTERN); | 
|  | clear_and_enable_ecc(); | 
|  |  | 
|  | return ret; | 
|  | } | 
|  |  | 
|  | int ecc_post_test(int flags) | 
|  | { | 
|  | int ret = 0; | 
|  | uint32_t value; | 
|  | uint32_t iaddr; | 
|  |  | 
|  | mfsdram(DDR0_22, value); | 
|  | if (0x3 != DDR0_22_CTRL_RAW_DECODE(value)) { | 
|  | debug("SDRAM ECC not enabled, skipping ECC POST.\n"); | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | /* Mask all interrupts. */ | 
|  | mfsdram(DDR0_01, value); | 
|  | mtsdram(DDR0_01, (value & ~DDR0_01_INT_MASK_MASK) | 
|  | | DDR0_01_INT_MASK_ALL_OFF); | 
|  |  | 
|  | for (iaddr = ECC_START_ADDR; iaddr <= ECC_STOP_ADDR; iaddr += iaddr) { | 
|  | ret = test_ecc(iaddr); | 
|  | if (ret) | 
|  | break; | 
|  | } | 
|  | /* | 
|  | * Clear possible errors resulting from ECC testing.  (If not done, we | 
|  | * we could get an interrupt later on when exceptions are enabled.) | 
|  | */ | 
|  | set_mcsr(get_mcsr()); | 
|  | debug("ecc_post_test() returning %d\n", ret); | 
|  | return ret; | 
|  | } | 
|  | #endif /* CONFIG_POST & CONFIG_SYS_POST_ECC */ | 
|  | #endif /* defined(CONFIG_440EPX) || defined(CONFIG_440GRX) */ |