| /* | 
 |  * (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 <asm/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) */ |