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