[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