[U-Boot] [PATCH 3/7] 83xx/85xx/86xx: Add ECC support
Peter Tyser
ptyser at xes-inc.com
Tue Nov 10 00:37:06 CET 2009
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
More information about the U-Boot
mailing list