diff options
Diffstat (limited to 'post/cpu/mpc83xx/ecc.c')
-rw-r--r-- | post/cpu/mpc83xx/ecc.c | 166 |
1 files changed, 166 insertions, 0 deletions
diff --git a/post/cpu/mpc83xx/ecc.c b/post/cpu/mpc83xx/ecc.c new file mode 100644 index 0000000..de87339 --- /dev/null +++ b/post/cpu/mpc83xx/ecc.c @@ -0,0 +1,166 @@ +/* + * (C) Copyright 2010 + * Eastman Kodak Company, <www.kodak.com> + * Michael Zaidman, <michael.zaidman@kodak.com> + * + * The code is based on the cpu/mpc83xx/ecc.c written by + * Dave Liu <daveliu@freescale.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 + */ + +#include <common.h> +#include <mpc83xx.h> +#include <watchdog.h> +#include <asm/io.h> +#include <post.h> + +#if CONFIG_POST & CONFIG_SYS_POST_ECC +/* + * We use the RAW I/O accessors where possible in order to + * achieve performance goal, since the test's execution time + * affects the board start up time. + */ +static inline void ecc_clear(ddr83xx_t *ddr) +{ + /* Clear capture registers */ + __raw_writel(0, &ddr->capture_address); + __raw_writel(0, &ddr->capture_data_hi); + __raw_writel(0, &ddr->capture_data_lo); + __raw_writel(0, &ddr->capture_ecc); + __raw_writel(0, &ddr->capture_attributes); + + /* Clear SBEC and set SBET to 1 */ + out_be32(&ddr->err_sbe, 1 << ECC_ERROR_MAN_SBET_SHIFT); + + /* Clear Error Detect register */ + out_be32(&ddr->err_detect, ECC_ERROR_DETECT_MME |\ + ECC_ERROR_DETECT_MBE |\ + ECC_ERROR_DETECT_SBE |\ + ECC_ERROR_DETECT_MSE); + + isync(); +} + +int ecc_post_test(int flags) +{ + int ret = 0; + int int_state; + int errbit; + u32 pattern[2], writeback[2], retval[2]; + ddr83xx_t *ddr = &((immap_t *)CONFIG_SYS_IMMR)->ddr; + volatile u64 *addr = (u64 *)CONFIG_SYS_POST_ECC_START_ADDR; + + /* The pattern is written into memory to generate error */ + pattern[0] = 0xfedcba98UL; + pattern[1] = 0x76543210UL; + + /* After injecting error, re-initialize the memory with the value */ + writeback[0] = ~pattern[0]; + writeback[1] = ~pattern[1]; + + /* Check if ECC is enabled */ + if (__raw_readl(&ddr->err_disable) & ECC_ERROR_ENABLE) { + debug("DDR's ECC is not enabled, skipping the ECC POST.\n"); + return 0; + } + + int_state = disable_interrupts(); + icache_enable(); + +#ifdef CONFIG_DDR_32BIT + /* It seems like no one really uses the CONFIG_DDR_32BIT mode */ +#error "Add ECC POST support for CONFIG_DDR_32BIT here!" +#else + for (addr = (u64*)CONFIG_SYS_POST_ECC_START_ADDR, errbit=0; + addr < (u64*)CONFIG_SYS_POST_ECC_STOP_ADDR; addr++, errbit++ ) { + + WATCHDOG_RESET(); + + ecc_clear(ddr); + + /* Enable error injection */ + setbits_be32(&ddr->ecc_err_inject, ECC_ERR_INJECT_EIEN); + sync(); + isync(); + + /* Set bit to be injected */ + if (errbit < 32) { + __raw_writel(1 << errbit, &ddr->data_err_inject_lo); + __raw_writel(0, &ddr->data_err_inject_hi); + } else { + __raw_writel(0, &ddr->data_err_inject_lo); + __raw_writel(1<<(errbit-32), &ddr->data_err_inject_hi); + } + sync(); + isync(); + + /* Write memory location injecting SBE */ + ppcDWstore((u32*)addr, pattern); + sync(); + + /* Disable error injection */ + clrbits_be32(&ddr->ecc_err_inject, ECC_ERR_INJECT_EIEN); + sync(); + isync(); + + /* Data read should generate SBE */ + ppcDWload((u32*)addr, retval); + sync(); + + if (!(__raw_readl(&ddr->err_detect) & ECC_ERROR_DETECT_SBE) || + (__raw_readl(&ddr->data_err_inject_hi) != + (__raw_readl(&ddr->capture_data_hi) ^ pattern[0])) || + (__raw_readl(&ddr->data_err_inject_lo) != + (__raw_readl(&ddr->capture_data_lo) ^ pattern[1]))) { + + post_log("ECC failed to detect SBE error at %08x, " + "SBE injection mask %08x-%08x, wrote " + "%08x-%08x, read %08x-%08x\n", addr, + ddr->data_err_inject_hi, + ddr->data_err_inject_lo, + pattern[0], pattern[1], + retval[0], retval[1]); + + printf("ERR_DETECT Reg: %08x\n", ddr->err_detect); + printf("ECC CAPTURE_DATA Reg: %08x-%08x\n", + ddr->capture_data_hi, ddr->capture_data_lo); + ret = 1; + break; + } + + /* Re-initialize the ECC memory */ + ppcDWstore((u32*)addr, writeback); + sync(); + isync(); + + errbit %= 63; + } +#endif /* !CONFIG_DDR_32BIT */ + + ecc_clear(ddr); + + icache_disable(); + + if (int_state) + enable_interrupts(); + + return ret; +} +#endif |