[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