[U-Boot] [PATCH 1/5] 8xxx: Add 'ecc' command

Peter Tyser ptyser at xes-inc.com
Fri Oct 23 02:39:09 CEST 2009


Add a new 'ecc' command to interact with the 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)

Signed-off-by: Peter Tyser <ptyser at xes-inc.com>
Signed-off-by: John Schmoller <jschmoller at xes-inc.com>
---
This code was tested on a 8572, 8640, and P2020.  A board with a
32-bit data bus was not tested however.

 cpu/mpc8xxx/ddr/Makefile     |    2 +
 cpu/mpc8xxx/ddr/ecc.c        |  371 ++++++++++++++++++++++++++++++++++++++++++
 include/asm-ppc/immap_85xx.h |    4 +
 include/asm-ppc/immap_86xx.h |    3 +
 4 files changed, 380 insertions(+), 0 deletions(-)
 create mode 100644 cpu/mpc8xxx/ddr/ecc.c

diff --git a/cpu/mpc8xxx/ddr/Makefile b/cpu/mpc8xxx/ddr/Makefile
index cb7f856..f073779 100644
--- a/cpu/mpc8xxx/ddr/Makefile
+++ b/cpu/mpc8xxx/ddr/Makefile
@@ -22,6 +22,8 @@ COBJS-$(CONFIG_FSL_DDR3)	+= main.o util.o ctrl_regs.o options.o \
 				   lc_common_dimm_params.o
 COBJS-$(CONFIG_FSL_DDR3)	+= ddr3_dimm_params.o
 
+COBJS-$(CONFIG_DDR_ECC_CMD)	+= ecc.o
+
 SRCS	:= $(START:.o=.S) $(SOBJS-y:.o=.S) $(COBJS-y:.o=.c)
 OBJS	:= $(addprefix $(obj),$(SOBJS-y) $(COBJS-y))
 
diff --git a/cpu/mpc8xxx/ddr/ecc.c b/cpu/mpc8xxx/ddr/ecc.c
new file mode 100644
index 0000000..bc80d7c
--- /dev/null
+++ b/cpu/mpc8xxx/ddr/ecc.c
@@ -0,0 +1,371 @@
+/*
+ * 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>
+#endif
+
+/* Provide a common define for DDR addresses on 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
+#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;
+			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;
+				break;
+			}
+		}
+	}
+
+	/* Print expected data */
+	if (bad_data_bit != -1) {
+		data_hi ^= 1 << (bad_data_bit - 32),
+		data_lo ^= 1 << bad_data_bit;
+	}
+	printf("\tExpect:\t0x%08x_%08x", data_hi, data_lo);
+
+	/* Print expected ECC code */
+	if (bad_ecc_bit != -1)
+		ecc ^= (1 << bad_ecc_bit) & 0xff;
+	printf("\tECC:\t0x%02x\n", 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"
+);
diff --git a/include/asm-ppc/immap_85xx.h b/include/asm-ppc/immap_85xx.h
index 4194295..b283986 100644
--- a/include/asm-ppc/immap_85xx.h
+++ b/include/asm-ppc/immap_85xx.h
@@ -167,6 +167,10 @@ typedef struct ccsr_ddr {
 	u32	data_err_inject_hi;	/* Data Path Err Injection Mask High */
 	u32	data_err_inject_lo;	/* Data Path Err Injection Mask Low */
 	u32	ecc_err_inject;		/* Data Path Err Injection Mask ECC */
+#define ECC_ERR_INJECT_APIEN	0x00010000	/* Address Parity Injection */
+#define ECC_ERR_INJECT_EMB	0x00000200	/* ECC Mirror Byte */
+#define ECC_ERR_INJECT_EIEN	0x00000100	/* Error Injection Enable */
+#define ECC_ERR_INJECT_EEIM	0x000000ff	/* ECC Erroe Injection Enable */
 	u8	res9[20];
 	u32	capture_data_hi;	/* Data Path Read Capture High */
 	u32	capture_data_lo;	/* Data Path Read Capture Low */
diff --git a/include/asm-ppc/immap_86xx.h b/include/asm-ppc/immap_86xx.h
index fdfc654..384912f 100644
--- a/include/asm-ppc/immap_86xx.h
+++ b/include/asm-ppc/immap_86xx.h
@@ -135,6 +135,9 @@ typedef struct ccsr_ddr {
 	uint	data_err_inject_hi;	/* 0x2e00 - DDR Memory Data Path Error Injection Mask High */
 	uint	data_err_inject_lo;	/* 0x2e04 - DDR Memory Data Path Error Injection Mask Low */
 	uint	ecc_err_inject;		/* 0x2e08 - DDR Memory Data Path Error Injection Mask ECC */
+#define ECC_ERR_INJECT_EMB	0x00000200	/* ECC Mirror Byte */
+#define ECC_ERR_INJECT_EIEN	0x00000100	/* Error Injection Enable */
+#define ECC_ERR_INJECT_EEIM	0x000000ff	/* ECC Erroe Injection Enable */
 	char	res12[20];
 	uint	capture_data_hi;	/* 0x2e20 - DDR Memory Data Path Read Capture High */
 	uint	capture_data_lo;	/* 0x2e24 - DDR Memory Data Path Read Capture Low */
-- 
1.6.2.1



More information about the U-Boot mailing list