How to use these command to test the ECC? Specially, how to inject multi error in 64bit data bus?
Thanks, Dave > -----Original Message----- > From: u-boot-boun...@lists.denx.de > [mailto:u-boot-boun...@lists.denx.de] On Behalf Of Peter Tyser > Sent: Tuesday, November 10, 2009 7:37 AM > To: u-boot@lists.denx.de > Cc: Peter Tyser; Phillips Kim-R1AAHA; ga...@kernel.crashing.org > Subject: [U-Boot] [PATCH 3/7] 83xx/85xx/86xx: Add ECC support > > Add a new 'ecc' command to interact with the 83xx, 85xx, and 86xx DDR > ECC registers. The 'ecc' command can inject data/ECC errors > to simulate > errors and provides an 'info' subcommand which displays ECC error > information such as failure address, read vs expected data/ECC, > physical signal which failed, single-bit error count, and multiple bit > error occurrence. An example of the 'ecc info' command follows: > > WARNING: ECC error in DDR Controller 0 > Addr: 0x0_01001000 > Data: 0x00000001_00000000 ECC: 0x00 > Expect: 0x00000000_00000000 ECC: 0x00 > Net: DATA32 > Syndrome: 0xce > Single-Bit errors: 0x40 > Attrib: 0x30112001 > Detect: 0x80000004 (MME, SBE) > > This new 'ecc' command support was placed in a new drives/edac > directory which can be used to support general Error Detection and > Correction (EDAC) code similar to Linux. > > Signed-off-by: Peter Tyser <pty...@xes-inc.com> > Signed-off-by: John Schmoller <jschmol...@xes-inc.com> > --- > Makefile | 2 + > drivers/edac/Makefile | 46 +++++ > drivers/edac/fsl_8xxx_ecc.c | 381 > +++++++++++++++++++++++++++++++++++++++++++ > 3 files changed, 429 insertions(+), 0 deletions(-) > create mode 100644 drivers/edac/Makefile > create mode 100644 drivers/edac/fsl_8xxx_ecc.c > > diff --git a/Makefile b/Makefile > index bed9469..3663780 100644 > --- a/Makefile > +++ b/Makefile > @@ -204,6 +204,7 @@ LIBS += disk/libdisk.a > LIBS += drivers/bios_emulator/libatibiosemu.a > LIBS += drivers/block/libblock.a > LIBS += drivers/dma/libdma.a > +LIBS += drivers/edac/libedac.a > LIBS += drivers/fpga/libfpga.a > LIBS += drivers/gpio/libgpio.a > LIBS += drivers/hwmon/libhwmon.a > @@ -416,6 +417,7 @@ TAG_SUBDIRS += disk > TAG_SUBDIRS += common > TAG_SUBDIRS += drivers/bios_emulator > TAG_SUBDIRS += drivers/block > +TAG_SUBDIRS += drivers/edac > TAG_SUBDIRS += drivers/gpio > TAG_SUBDIRS += drivers/hwmon > TAG_SUBDIRS += drivers/i2c > diff --git a/drivers/edac/Makefile b/drivers/edac/Makefile > new file mode 100644 > index 0000000..92ab497 > --- /dev/null > +++ b/drivers/edac/Makefile > @@ -0,0 +1,46 @@ > +# > +# (C) Copyright 2000-2007 > +# Wolfgang Denk, DENX Software Engineering, w...@denx.de. > +# > +# 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 $(TOPDIR)/config.mk > + > +LIB := $(obj)libedac.a > + > +COBJS-$(CONFIG_EDAC_FSL_ECC) += fsl_8xxx_ecc.o > + > +COBJS := $(COBJS-y) > +SRCS := $(COBJS:.o=.c) > +OBJS := $(addprefix $(obj),$(COBJS)) > + > +all: $(LIB) > + > +$(LIB): $(obj).depend $(OBJS) > + $(AR) $(ARFLAGS) $@ $(OBJS) > + > +############################################################# > ############ > + > +# defines $(obj).depend target > +include $(SRCTREE)/rules.mk > + > +sinclude $(obj).depend > + > +############################################################# > ############ > diff --git a/drivers/edac/fsl_8xxx_ecc.c b/drivers/edac/fsl_8xxx_ecc.c > new file mode 100644 > index 0000000..8f3bc7e > --- /dev/null > +++ b/drivers/edac/fsl_8xxx_ecc.c > @@ -0,0 +1,381 @@ > +/* > + * Copyright 2009 Extreme Engineering Solutions, Inc. > + * > + * 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 <asm/io.h> > +#if defined(CONFIG_MPC85xx) > +#include <mpc85xx.h> > +#elif defined(CONFIG_MPC86xx) > +#include <mpc86xx.h> > +#elif defined(CONFIG_MPC83xx) > +#include <mpc83xx.h> > +#endif > + > +/* Provide a common name for DDR addresses on 83xx/85xx/86xx */ > +#if defined(CONFIG_MPC85xx) > +#define MPC8xxx_DDR_ADDR CONFIG_SYS_MPC85xx_DDR_ADDR > +#define MPC8xxx_DDR2_ADDR CONFIG_SYS_MPC85xx_DDR2_ADDR > +#elif defined(CONFIG_MPC86xx) > +#define MPC8xxx_DDR_ADDR CONFIG_SYS_MPC86xx_DDR_ADDR > +#define MPC8xxx_DDR2_ADDR CONFIG_SYS_MPC86xx_DDR2_ADDR > +#elif defined(CONFIG_MPC83xx) > +#define MPC8xxx_DDR_ADDR CONFIG_SYS_MPC83xx_DDR_ADDR > +#define MPC8xxx_DDR2_ADDR CONFIG_SYS_MPC83xx_DDR2_ADDR > +/* 83xx uses a different structure name, but has the same > register names */ > +typedef ddr83xx_t ccsr_ddr_t; > + > +/* some 83xx support 2 controllers, but no boards use the > 2nd one yet */ > +#ifndef CONFIG_NUM_DDR_CONTROLLERS > +#define CONFIG_NUM_DDR_CONTROLLERS 1 > +#endif > +#else > +#error "ECC not supported on this platform" > +#endif > + > +/* > + * Taken from table 8-55 in the MPC8641 User's Manual and/or > 9-61 in the > + * MPC8572 User's Manual. Each line represents a syndrome > bit column as a > + * 64-bit value, but split into an upper and lower 32-bit > chunk. The labels > + * below correspond to Freescale's manuals. > + */ > +static unsigned int ecc_table[16] = { > + /* MSB LSB */ > + /* [0:31] [32:63] */ > + 0xf00fe11e, 0xc33c0ff7, /* Syndrome bit 7 */ > + 0x00ff00ff, 0x00fff0ff, > + 0x0f0f0f0f, 0x0f0fff00, > + 0x11113333, 0x7777000f, > + 0x22224444, 0x8888222f, > + 0x44448888, 0xffff4441, > + 0x8888ffff, 0x11118882, > + 0xffff1111, 0x22221114, /* Syndrome bit 0 */ > +}; > + > +/* > + * Calculate the correct ECC value for a 64-bit integer > specified by high:low > + */ > +static uint8_t calculate_ecc(uint32_t high, uint32_t low) > +{ > + uint32_t mask_low; > + uint32_t mask_high; > + int bit_cnt; > + uint8_t ecc = 0; > + int i; > + int j; > + > + for (i = 0; i < 8; i++) { > + mask_high = ecc_table[i * 2]; > + mask_low = ecc_table[i * 2 + 1]; > + bit_cnt = 0; > + > + for (j = 0; j < 32; j++) { > + if ((mask_high >> j) & 1) > + bit_cnt ^= (high >> j) & 1; > + if ((mask_low >> j) & 1) > + bit_cnt ^= (low >> j) & 1; > + } > + > + ecc |= bit_cnt << i; > + } > + > + return ecc; > +} > + > +/* > + * Create the syndrome code which is generated if the data > line specified by > + * 'bit' failed. Eg generate the 8-bit codes seen in Table > 8-55 in the MPC8641 > + * User's Manual and 9-61 in the MPC8572 User's Manual. > + */ > +static uint8_t syndrome_from_bit(unsigned int bit) { > + int i; > + uint8_t syndrome = 0; > + > + /* > + * Cycle through the upper or lower 32-bit portion of > each value in > + * ecc_table depending on if 'bit' is in the upper or > lower half of > + * 64-bit data. > + */ > + for (i = bit < 32; i < 16; i += 2) > + syndrome |= ((ecc_table[i] >> (bit % 32)) & 1) > << (i / 2); > + > + return syndrome; > +} > + > +/* > + * Decode data and ecc syndrome to determine what went wrong > + * Note: This can only decode single-bit errors > + */ > +static void ecc_decode(uint32_t data_hi, uint32_t data_lo, > uint32_t ecc) > +{ > + int i; > + int bad_data_bit = -1; > + int bad_ecc_bit = -1; > + uint8_t syndrome; > + > + /* > + * Calculate the ECC of the captured data and XOR it > with the captured > + * ECC to find an ECC syndrome value > + */ > + syndrome = calculate_ecc(data_hi, data_lo) ^ ecc; > + > + /* Check if a data line is stuck... */ > + for (i = 0; i < 64; i++) { > + if (syndrome == syndrome_from_bit(i)) { > + /* > + * Save absolute position for printing > out later. > + * Note that we are recording the bit > failure address > + * with respect to the DRAM chips, not > the CPU. For > + * example, if the least significant > data bit fails, > + * we print out that DATA0 failed, not > DATA63 which is > + * how the CPU pinout is labeled. > + */ > + bad_data_bit = i; > + > + /* Convert 'data_[hi|low]' to their > expected value */ > + data_hi ^= 1 << (bad_data_bit - 32), > + data_lo ^= 1 << bad_data_bit; > + break; > + } > + } > + > + /* If data is correct, check ECC bits for errors... */ > + if (bad_data_bit == -1) { > + for (i = 0; i < 8; i++) { > + /* > + * Note that we are recording the bit > failure address > + * with respect to the DRAM chips, not > the CPU. For > + * example, if the least significant > ECC bit fails, > + * we print out that ECC0 failed, not > ECC7 which is > + * how the CPU pinout is labeled. See > Table 9-62 in > + * the MPC8572 user's manual for > additional details. > + */ > + if ((syndrome >> i) & 0x1) { > + bad_ecc_bit = i; > + > + /* Convert 'ecc' to its > expected value */ > + ecc ^= (1 << bad_ecc_bit) & 0xff; > + break; > + } > + } > + } > + > + /* Print expected data and ecc */ > + printf("\tExpect:\t0x%08x_%08x\tECC:\t0x%02x\n", > data_hi, data_lo, ecc); > + > + /* Print data or ECC net which caused ECC error */ > + if (bad_ecc_bit != -1) > + printf("\tNet:\tECC%d\n", bad_ecc_bit); > + else > + printf("\tNet:\tDATA%d\n", bad_data_bit); > + > + printf("\tSyndrome: 0x%x\n", syndrome); > +} > + > +int ecc_count(void) > +{ > + int count = 0; > + int i; > + volatile ccsr_ddr_t* ddr[] = { > + (void*)MPC8xxx_DDR_ADDR, > +#if (CONFIG_NUM_DDR_CONTROLLERS > 1) > + (void*)MPC8xxx_DDR2_ADDR, > +#endif > + }; > + > + for (i = 0; i < ARRAY_SIZE(ddr); i++) { > + /* Add up single-bit errors */ > + count += in_be32(&ddr[i]->err_sbe) & 0xff; > + > + /* Add 1 for a multiple-bit error */ > + if (in_be32(&ddr[i]->err_detect) & 0x8) > + count++; > + } > + > + return count; > +} > + > +void ecc_info(void) > +{ > + int controller; > + uint32_t data_hi; > + uint32_t data_lo; > + uint32_t ecc; > + uint32_t err_detect; > + uint32_t sbe; > + uint32_t attributes; > + uint32_t address; > + uint32_t ext_address; > + volatile ccsr_ddr_t* ddr[] = { > + (void*)MPC8xxx_DDR_ADDR, > +#if (CONFIG_NUM_DDR_CONTROLLERS > 1) > + (void*)MPC8xxx_DDR2_ADDR, > +#endif > + }; > + > + for (controller = 0; > + controller < CONFIG_NUM_DDR_CONTROLLERS; > + controller ++) { > + /* Check for a single or multiple-bit ECC error */ > + sbe = in_be32(&ddr[controller]->err_sbe) & 0xff; > + err_detect = in_be32(&ddr[controller]->err_detect); > + if (!sbe && !(err_detect & 0x8)) > + continue; > + > + /* Read in the rest of the ECC error info */ > + data_hi = in_be32(&ddr[controller]->capture_data_hi); > + data_lo = in_be32(&ddr[controller]->capture_data_lo), > + ecc = in_be32(&ddr[controller]->capture_ecc) & 0xff; > + attributes = > in_be32(&ddr[controller]->capture_attributes); > + address = in_be32(&ddr[controller]->capture_address); > + ext_address = > in_be32(&ddr[controller]->capture_ext_address) & 0xf; > + > + printf("\nWARNING: ECC error in DDR Controller > %d\n", controller); > + printf("\tAddr:\t0x%01x_%08x\n", ext_address, address); > + printf("\tData:\t0x%08x_%08x\tECC:\t0x%02x\n", > + data_hi, data_lo, ecc); > + > + if (err_detect & 0x8) { > + printf("ERROR: Multiple-bit errors!!!\n"); > + } else if (sbe) { > + /* Analyze which data or ecc line is > the culprit. */ > + ecc_decode(data_hi, data_lo, ecc); > + } > + > + printf("\tSingle-Bit errors: 0x%02x\n", sbe); > + printf("\tAttrib:\t0x%08x\n", attributes); > + printf("\tDetect:\t0x%08x", err_detect); > + if(err_detect) > + printf(" ("); > + if(err_detect & 0x80000000) > + printf("MME, "); > + if(err_detect & 0x100) > + printf("APE, "); > + if(err_detect & 0x80) > + printf("ACE, "); > + if(err_detect & 0x8) > + printf("MBE, "); > + if(err_detect & 0x4) > + printf("SBE, "); > + if(err_detect & 0x1) > + printf("MSE, "); > + if(err_detect) > + printf("\b\b)\n"); > + > + printf("\n"); > + > + /* Clear ECC error info and count */ > + out_be32(&ddr[controller]->err_detect, > + in_be32(&ddr[controller]->err_detect)); > + out_be32(&ddr[controller]->err_sbe, > + in_be32(&ddr[controller]->err_sbe) & ~0xff); > + } > +} > + > +static int do_ecc(cmd_tbl_t * cmdtp, int flag, int argc, > char *argv[]) > +{ > + static uint controller = 0; > + uint32_t ecc_mask = 0; > + uint32_t mask; > + int count; > + volatile ccsr_ddr_t* ddr[] = { > + (void*)MPC8xxx_DDR_ADDR, > +#if (CONFIG_NUM_DDR_CONTROLLERS > 1) > + (void*)MPC8xxx_DDR2_ADDR, > +#endif > + }; > + > + if (argc == 2) { > + if (argv[1][0] == 'i') { > + /* 'ecc info' */ > + count = ecc_count(); > + if (count) { > + ecc_info(); > + return count; > + } > + > + printf("No ECC errors have occurred\n"); > + > + return 0; > + } else if (argv[1][0] == 'c') { > + /* 'ecc ctrl' */ > + printf("Active controller: %d\n", controller); > + return 0; > + } > + } else if (argc == 3) { > + if (argv[1][0] == 'c') { > + /* 'ecc ctrl num' */ > + controller = simple_strtoul(argv[2], NULL, 16); > + if (controller > > (CONFIG_NUM_DDR_CONTROLLERS - 1)) { > + printf("Invalid controller number\n"); > + controller = 0; > + return 1; > + } > + return 0; > + } else if ((argv[1][0] == 'i') && (argv[2][0] == 'o')) { > + /* 'ecc inject off' */ > + out_be32(&ddr[controller]->ecc_err_inject, 0); > + > out_be32(&ddr[controller]->data_err_inject_lo, 0); > + > out_be32(&ddr[controller]->data_err_inject_hi, 0); > + return 0; > + } > + } else if (argc == 4) { > + /* 'ecc inject <high|low|ecc> mask' */ > + mask = simple_strtoul(argv[3], NULL, 16); > + switch (argv[2][0]) { > + case 'h': > + > out_be32(&ddr[controller]->data_err_inject_hi, mask); > + break; > + case 'l': > + > out_be32(&ddr[controller]->data_err_inject_lo, mask); > + break; > + case 'e': > + ecc_mask = mask & ECC_ERR_INJECT_EEIM; > + break; > + default: > + cmd_usage(cmdtp); > + return 1; > + } > + > + /* Enable error injection */ > + out_be32(&ddr[controller]->ecc_err_inject, > + ecc_mask | ECC_ERR_INJECT_EIEN); > + return 0; > + } > + > + cmd_usage(cmdtp); > + return 1; > +} > + > +U_BOOT_CMD(ecc, 5, 0, do_ecc, > + "support for DDR ECC features", > + "info - print ECC information\n" > +#if (CONFIG_NUM_DDR_CONTROLLERS > 1) > + "ecc ctrl [num]\n" > + "\t-Set active controller to 'num', or display active > controller\n" > +#endif > + "ecc inject high <mask>\n" > + "ecc inject low <mask>\n" > + "ecc inject ecc <mask>\n" > + "\t- XOR 'mask' with high/low data or ECC\n" > + "ecc inject off\n" > + "\t- disable error injection\n" > +); > -- > 1.6.2.1 > > _______________________________________________ > U-Boot mailing list > U-Boot@lists.denx.de > http://lists.denx.de/mailman/listinfo/u-boot > > _______________________________________________ U-Boot mailing list U-Boot@lists.denx.de http://lists.denx.de/mailman/listinfo/u-boot