[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