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 <pty...@xes-inc.com>
Signed-off-by: John Schmoller <jschmol...@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

_______________________________________________
U-Boot mailing list
U-Boot@lists.denx.de
http://lists.denx.de/mailman/listinfo/u-boot

Reply via email to