[U-Boot] [PATCH 3/7] 83xx/85xx/86xx: Add ECC support

Liu Dave-R63238 r63238 at freescale.com
Tue Nov 10 01:25:04 CET 2009


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-bounces at lists.denx.de 
> [mailto:u-boot-bounces at lists.denx.de] On Behalf Of Peter Tyser
> Sent: Tuesday, November 10, 2009 7:37 AM
> To: u-boot at lists.denx.de
> Cc: Peter Tyser; Phillips Kim-R1AAHA; galak at 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 <ptyser at xes-inc.com>
> Signed-off-by: John Schmoller <jschmoller at 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, wd at 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 at lists.denx.de
> http://lists.denx.de/mailman/listinfo/u-boot
> 
> 


More information about the U-Boot mailing list